tag:blogger.com,1999:blog-48876090885537853942024-03-21T20:15:37.596-07:00Dragon Quest 64Personal software engineering blog where I share a variety of topics that interest me.Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.comBlogger150125tag:blogger.com,1999:blog-4887609088553785394.post-83114434092736149912024-02-01T19:18:00.000-08:002024-02-01T19:21:23.329-08:00Yolo AutoCropping Presentation Videos<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjclVmaCt8FZwiLgXvdyzGa8jzpW-coMp3ouc-_DxfR7UuBOq2mJlN93SHzuMSSAP-BH1c6rhug8xsd8-xelTnYELN1HzklGTcBU7pw3HaoZetQwZFJ4aWQ42TIoMExK6RgX7dh4rNUNWC7z_M7uuXiC_GcpYQLswJG-DlEf7OTZIeVDVC7jyHjoPXCUcPu/s632/Screen-shot-2014-10-02-at-3.12.09-AM.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="198" data-original-width="632" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjclVmaCt8FZwiLgXvdyzGa8jzpW-coMp3ouc-_DxfR7UuBOq2mJlN93SHzuMSSAP-BH1c6rhug8xsd8-xelTnYELN1HzklGTcBU7pw3HaoZetQwZFJ4aWQ42TIoMExK6RgX7dh4rNUNWC7z_M7uuXiC_GcpYQLswJG-DlEf7OTZIeVDVC7jyHjoPXCUcPu/w640-h200/Screen-shot-2014-10-02-at-3.12.09-AM.png" width="640" /></a></div><br /> <p></p><p>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.</p><p>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.</p><p><br /></p><p><a href="https://pjreddie.com/darknet/yolo/">YOLO</a> (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. </p><p> </p><p>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 <i>snapping to the presenter location,</i> 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.</p><p>Each mechanism is a rough implementation, focused on rapid proof-of-concept rather than optimal results, but you get the idea. </p><p><br /></p><p>The source video was found on here; <a href="https://youtu.be/SONHnq_2yxY">Minnebar7</a></p><p> <br /></p><p> <br /></p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="337" src="https://www.youtube.com/embed/RQ6gwvv09CA" width="405" youtube-src-id="RQ6gwvv09CA"></iframe></div><p></p><p><br /></p><p><br /></p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-33231144531380153542024-01-30T20:19:00.000-08:002024-01-30T20:19:00.964-08:00Published My First Python Package<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHcqmW94M2D952D9S16OHwda35aZ0NsevbP1t97ddc-lES-S6T9LF8Dcv2w4Niu_JqwNFPJWY42n6jcmwZtTxIKrolL1iSgPQXLGQm4mywAjYxtIPMYS5RPEhgByEW8LuiKD15lYnyDV7fVYK9UWNmdNKn9UDK9YzUWCbSukgrMIaOYsDMt-sMt6L9Niv0/s1136/564cc42e2491f99d008b63b7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="852" data-original-width="1136" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHcqmW94M2D952D9S16OHwda35aZ0NsevbP1t97ddc-lES-S6T9LF8Dcv2w4Niu_JqwNFPJWY42n6jcmwZtTxIKrolL1iSgPQXLGQm4mywAjYxtIPMYS5RPEhgByEW8LuiKD15lYnyDV7fVYK9UWNmdNKn9UDK9YzUWCbSukgrMIaOYsDMt-sMt6L9Niv0/w640-h480/564cc42e2491f99d008b63b7.jpg" width="640" /></a></div><br /> <p></p><p> </p><p>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.</p><p>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. <br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw3hZJGKo-ttX-gfmAZA8brhh9PffK7a7zVOKrTUDWkl4MUfR2LOphyphenhyphenYbDYb8KU9Iz-rDP073cXYUquXJBi5CHLHiWBmiadNz_u36437bqI-gnSq6kA1oiIldA8sDqRl0Hnqhf1glNcnLErSUPk0QNNlLBFRbkPbbT2mh0mqAihQY0KLo69VZ5-KoWg9YC/s3024/Science_Museum_20180227_132902_(49362732462).jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2954" data-original-width="3024" height="313" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw3hZJGKo-ttX-gfmAZA8brhh9PffK7a7zVOKrTUDWkl4MUfR2LOphyphenhyphenYbDYb8KU9Iz-rDP073cXYUquXJBi5CHLHiWBmiadNz_u36437bqI-gnSq6kA1oiIldA8sDqRl0Hnqhf1glNcnLErSUPk0QNNlLBFRbkPbbT2mh0mqAihQY0KLo69VZ5-KoWg9YC/s320/Science_Museum_20180227_132902_(49362732462).jpg" width="320" /></a></div><p>A customized version of Fortran, its vocabulary, the Computer Science department rarely used it, aerospace and atmospheric sciences most heavily used the system.</p><p>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.</p><p>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.</p><p><a href="https://github.com/lipeltgm/dividere/blob/main/main.pdf">dividere UG</a><br /></p><p>The public project repository is located at:</p><p><a href="https://github.com/lipeltgm/dividere">https://github.com/lipeltgm/dividere</a></p><p><br /></p><p>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.</p><p>Until then, it likely will require manual installation of protobuff-v3.19 (or later) before installing via pip3 from pypi:</p><p><a href="https://pypi.org/project/dividere/">https://pypi.org/project/dividere/</a> </p><p>$ pip3 install dividere<br /></p><p><br /></p><p>With the foundation in place, I'm intending on extending the framework to support more reliable messaging, database components, robust failover detection and recovery. </p><p>More to come in the future, fingers-crossed.<br /></p><p><br /></p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-2186717024236643592024-01-29T07:00:00.000-08:002024-01-29T13:06:55.074-08:00Embarking on Authoring a Computer Science Book<p> </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://images.pexels.com/photos/13650913/pexels-photo-13650913.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="533" data-original-width="800" height="427" src="https://images.pexels.com/photos/13650913/pexels-photo-13650913.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" width="640" /></a></div><br />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. <p></p><p>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. </p><p>On and off, I return to this passion project unsure of it's practicality but it helps rekindle my love for this career.</p><p> </p><p>Attached is a snippet of my work in progress, not even titled yet; <br /></p><p><a href="https://drive.google.com/file/d/1FwGQeRwMYByiEYZa_tG0lRNiIAAUWP-j/view?usp=drive_link">WIP</a></p><p><br /></p><p>I'd welcome any feedback on the chapter as well as experiences from anyone who has authored and published a book.<br /></p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-68746834897151188032024-01-26T12:10:00.000-08:002024-01-30T06:35:23.520-08:00C++ Database ORM Project<p> </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLhFUpBmVKK0GqBmSfLp3bRdgAw1vY6hX_PE7JFI2EwyeC6M0qTE5dOE6dnPwGXXCBSYjCml5jbbhZ_PQrlfnb4iYtQ-Yq5cBqCWhoBVEA1QDw-3R8OMo9eM26YTWFH_YNnJo4RhM0woK_AaXiKi0SgvbLNYiScpl71lBjq66zUdp-LjnCmbHDW1f0QbIU/s600/Calvin_Hobbes_Data_Quality.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="191" data-original-width="600" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLhFUpBmVKK0GqBmSfLp3bRdgAw1vY6hX_PE7JFI2EwyeC6M0qTE5dOE6dnPwGXXCBSYjCml5jbbhZ_PQrlfnb4iYtQ-Yq5cBqCWhoBVEA1QDw-3R8OMo9eM26YTWFH_YNnJo4RhM0woK_AaXiKi0SgvbLNYiScpl71lBjq66zUdp-LjnCmbHDW1f0QbIU/w640-h204/Calvin_Hobbes_Data_Quality.gif" width="640" /></a></div><br /> <p></p><p>Systems that utilize a database benefit from an automated means of translating database CRUD operations to application language. </p><p>Products like 'ObjectStore' aim to provide mechanisms to exchange data to/from applications to the database in a seamless fashion.</p><p>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. </p><p>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.</p><p>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.</p><p></p><p>A db-object file (e.g. MyDb.odb) can specify a database object as follows:</p><p><span style="font-family: courier;">dbclass MyRecord002<br /> float val01 as key;<br />end;</span></p><p>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 <u>can</u> automatically be applied in the database. The linked association between the C++ object and the database are enforced by constructors and access methods.</p><p></p><p>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.</p><p></p><p><a href="https://github.com/fsk-software/pub/tree/master/DbObjOverlay/">https://github.com/fsk-software/pub/tree/master/DbObjOverlay</a><br /></p><p>Recently came across Wt::Dbo reference as well, haven't had a chance to take a look yet; <a href="https://www.webtoolkit.eu/wt/doc/tutorial/dbo.html">https://www.webtoolkit.eu/wt/doc/tutorial/dbo.html</a></p><p><br /></p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-87838424626992792132022-04-11T19:15:00.000-07:002022-04-11T19:15:59.599-07:00Extracting Video Information w/FFProbe<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkWUt3o_98xySi8Fj0Y6eHUQ_aJQe_z5yDJ-0c4op4MMo2e_v1Ley3fZ_X4_88P_5mgGZD6hSHzHvF2yTM2Ymo-V62cLBL_Thql7JPKJ-J_THWRGjTERMXCL4jkj4_IkZZ5cWLpmD0cKCdJkQUMtHVhnYUsytWO_la11T9Y56V6naPgnkijbvUiiUEwA/s1024/ffmpeg-logo-1024x274.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="274" data-original-width="1024" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkWUt3o_98xySi8Fj0Y6eHUQ_aJQe_z5yDJ-0c4op4MMo2e_v1Ley3fZ_X4_88P_5mgGZD6hSHzHvF2yTM2Ymo-V62cLBL_Thql7JPKJ-J_THWRGjTERMXCL4jkj4_IkZZ5cWLpmD0cKCdJkQUMtHVhnYUsytWO_la11T9Y56V6naPgnkijbvUiiUEwA/s320/ffmpeg-logo-1024x274.png" width="320" /></a></div><br /><div><br /></div><div>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.</div><div><br /></div><div>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.</div><div><br /></div><div>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.</div><div><br /></div><div>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. </div><div><br /></div><div>Let’s start by examining our media files container.</div><div><span style="font-family: courier; font-size: x-small;">$ ffprobe - loglevel quiet - show_format BigBuckBunny . mp4</span></div><div><span style="font-family: courier; font-size: x-small;">[ FORMAT ]</span></div><div><span style="font-family: courier; font-size: x-small;">filename = BigBuckBunny . mp4</span></div><div><span style="font-family: courier; font-size: x-small;">nb_streams =2</span></div><div><span style="font-family: courier; font-size: x-small;">nb_programs =0</span></div><div><span style="font-family: courier; font-size: x-small;">format_name = matroska , webm</span></div><div><span style="font-family: courier; font-size: x-small;">format_long_name = Matroska / WebM</span></div><div><span style="font-family: courier; font-size: x-small;">start_time = -0.007000</span></div><div><span style="font-family: courier; font-size: x-small;">duration =596.501000</span></div><div><span style="font-family: courier; font-size: x-small;">size =107903686</span></div><div><span style="font-family: courier; font-size: x-small;">bit_rate =1447155</span></div><div><span style="font-family: courier; font-size: x-small;">probe_score =100</span></div><div><span style="font-family: courier; font-size: x-small;">TAG : C OMPATI BLE_BR ANDS = iso6avc1mp41</span></div><div><span style="font-family: courier; font-size: x-small;">TAG : MAJOR_BRAND = dash</span></div><div><span style="font-family: courier; font-size: x-small;">TAG : MINOR_VERSION =0</span></div><div><span style="font-family: courier; font-size: x-small;">TAG : ENCODER = Lavf56 .40.101</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FORMAT ]</span></div><div><br /></div><div>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.</div><div><br /></div><div><span style="font-family: courier; font-size: x-small;">$ ffprobe - loglevel quiet - show_format BigBuckBunny .</span></div><div><span style="font-family: courier; font-size: x-small;">mp4 - show_entries format = duration , size</span></div><div><span style="font-family: courier; font-size: x-small;">[ FORMAT ]</span></div><div><span style="font-family: courier; font-size: x-small;">duration =596.501000</span></div><div><span style="font-family: courier; font-size: x-small;">size =107903686</span></div><div><span style="font-family: courier; font-size: x-small;">TAG : COMPATIBLE_BRANDS = iso6avc1mp41</span></div><div><span style="font-family: courier; font-size: x-small;">TAG : MAJOR_BRAND = dash</span></div><div><span style="font-family: courier; font-size: x-small;">TAG : MINOR_VERSION =0</span></div><div><span style="font-family: courier; font-size: x-small;">TAG : ENCODER = Lavf56 .40.101</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FORMAT ]</span></div><div><br /></div><div>Cool, but not particularly interesting, and really nothing a filemanager couldn’t show you with a simple right-click.</div><div><br /></div><div>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.</div><div><br /></div><div><span style="font-family: courier; font-size: x-small;">$ ffprobe - loglevel quiet - show_frames BigBuckBunny .</span></div><div><span style="font-family: courier; font-size: x-small;">mp4</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">media_type = video</span></div><div><span style="font-family: courier; font-size: x-small;">stream_index =0</span></div><div><span style="font-family: courier; font-size: x-small;">key_frame =1</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pts =0</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pts_time =0.000000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_dts =0</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_dts_time =0.000000</span></div><div><span style="font-family: courier; font-size: x-small;">best_effort_timestamp =0</span></div><div><span style="font-family: courier; font-size: x-small;">best_effort_timestamp_time =0.000000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_duration =41</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_durat ion_time =0.041000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pos =1111</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_size =208</span></div><div><span style="font-family: courier; font-size: x-small;">width =1280</span></div><div><span style="font-family: courier; font-size: x-small;">height =720</span></div><div><span style="font-family: courier; font-size: x-small;">pix_fmt = yuv420p</span></div><div><span style="font-family: courier; font-size: x-small;">sample_aspect_ratio =1:1</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = I</span></div><div><span style="font-family: courier; font-size: x-small;">coded_picture_number =0</span></div><div><span style="font-family: courier; font-size: x-small;">display_picture_number =0</span></div><div><span style="font-family: courier; font-size: x-small;">interlaced_frame =0</span></div><div><span style="font-family: courier; font-size: x-small;">top_field_first =0</span></div><div><span style="font-family: courier; font-size: x-small;">repeat_pict =0</span></div><div><span style="font-family: courier; font-size: x-small;">color_range = unknown</span></div><div><span style="font-family: courier; font-size: x-small;">color_space = unknown</span></div><div><span style="font-family: courier; font-size: x-small;">color_primaries = unknown</span></div><div><span style="font-family: courier; font-size: x-small;">color_transfer = unknown</span></div><div><span style="font-family: courier; font-size: x-small;">chroma_location = left</span></div><div><span style="font-family: courier; font-size: x-small;">[/FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">media_type = audio</span></div><div><span style="font-family: courier; font-size: x-small;">stream_index =1</span></div><div><span style="font-family: courier; font-size: x-small;">key_frame =1</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pts =0</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pts_time =0.000000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_dts =0</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_dts_time =0.000000</span></div><div><span style="font-family: courier; font-size: x-small;">best_effort_timestamp = -7</span></div><div><span style="font-family: courier; font-size: x-small;">best_effort_timestamp_time = -0.007000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_duration =13</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_duration_time =0.013000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pos =1368</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_size =3</span></div><div><span style="font-family: courier; font-size: x-small;">sample_fmt = fltp</span></div><div><span style="font-family: courier; font-size: x-small;">nb_samples =648</span></div><div><span style="font-family: courier; font-size: x-small;">channels =2</span></div><div><span style="font-family: courier; font-size: x-small;">channel_layout = stereo</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><br /></div><div>Notice that this snippet contains two frames, one video and one audio, each</div><div>with a set of media-specific fields. Collectively, we’re left with the follow-</div><div>ing collection of fields: <i>best effort timestamp, best effort timestamp time, chan-</i></div><div><i>nel layout, channels, chroma location, coded picture number, color primaries,</i></div><div><i>color range, color space, color transfer, display picture number, height, inter-</i></div><div><i>laced frame, key frame, media type, nb samples, pict type, pix fmt, pkt dts, pkt dts time,</i></div><div><i>pkt duration, pkt duration time, pkt pos, pkt pts, pkt pts time, pkt size, repeat pict,</i></div><div><i>sample aspect ratio, sample fmt, stream index, top field first, width</i>.</div><div><br /></div><div>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.</div><div>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.</div><div><br /></div><div><span style="font-family: courier; font-size: x-small;">$ ffprobe - loglevel quiet - select_streams V -</span></div><div><span style="font-family: courier; font-size: x-small;">show_frames BigBuckBunny . mp4[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">media_type = video</span></div><div><span style="font-family: courier; font-size: x-small;">stream_index =0</span></div><div><span style="font-family: courier; font-size: x-small;">key_frame =1</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pts =0</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pts_time =0.000000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_dts =0</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_dts_time =0.000000</span></div><div><span style="font-family: courier; font-size: x-small;">best_effort_timestamp =0</span></div><div><span style="font-family: courier; font-size: x-small;">best_effort_timestamp_time =0.000000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_duration =41</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_durat ion_ti me =0.041000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pos =1111</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_size =208</span></div><div><span style="font-family: courier; font-size: x-small;">width =1280</span></div><div><span style="font-family: courier; font-size: x-small;">height =720</span></div><div><span style="font-family: courier; font-size: x-small;">pix_fmt = yuv420p</span></div><div><span style="font-family: courier; font-size: x-small;">sample_aspect_ratio =1:1</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = I</span></div><div><span style="font-family: courier; font-size: x-small;">coded_picture_number =0</span></div><div><span style="font-family: courier; font-size: x-small;">display_picture_number =0</span></div><div><span style="font-family: courier; font-size: x-small;">interlaced_frame =0</span></div><div><span style="font-family: courier; font-size: x-small;">top_field_first =0</span></div><div><span style="font-family: courier; font-size: x-small;">repeat_pict =0</span></div><div><span style="font-family: courier; font-size: x-small;">color_range = unknown</span></div><div><span style="font-family: courier; font-size: x-small;">color_space = unknown</span></div><div><span style="font-family: courier; font-size: x-small;">color_primaries = unknown</span></div><div><span style="font-family: courier; font-size: x-small;">color_transfer = unknown</span></div><div><span style="font-family: courier; font-size: x-small;">chroma_location = left</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">media_type = video</span></div><div><span style="font-family: courier; font-size: x-small;">stream_index =0</span></div><div><span style="font-family: courier; font-size: x-small;">key_frame =0</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pts =42</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pts_time =0.042000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_dts =42</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_dts_time =0.042000</span></div><div><span style="font-family: courier; font-size: x-small;">best_effort_timestamp =42</span></div><div><span style="font-family: courier; font-size: x-small;">best_effort_timestamp_time =0.042000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_duration =41</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_duration_time =0.041000</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_pos =1325</span></div><div><span style="font-family: courier; font-size: x-small;">pkt_size =37</span></div><div><span style="font-family: courier; font-size: x-small;">width =1280</span></div><div><span style="font-family: courier; font-size: x-small;">height =720pix_fmt = yuv420p</span></div><div><span style="font-family: courier; font-size: x-small;">sample_aspect_ratio =1:1</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = P</span></div><div><br /></div><div><b>Packet Fields</b> 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.</div><div><b><br /></b></div><div><b>PictureType Field</b> 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-</div><div>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. <u>Pretty neat, huh?</u></div><div><u><br /></u></div><div><b>Timestamp Fields</b> 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</div><div>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.</div><div><br /></div><div>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.</div><div><br /></div><div><span style="font-family: courier; font-size: x-small;">$ ffprobe -loglevel quiet -select_streams V -show_frames -show_entries frame=pict_type BigBuckBunny.mp4</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = I</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = P</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = P</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = P</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = P</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = B</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = B</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = P</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = B</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">[ FRAME ]</span></div><div><span style="font-family: courier; font-size: x-small;">pict_type = P</span></div><div><span style="font-family: courier; font-size: x-small;">[/ FRAME ]</span></div><div><br /></div><div>Lovely, right?</div><div><br /></div><div>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</div><div>increasing timestamp) derived from available timestamp values. </div><div><br /></div><div><b>Video Size Fields</b> 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.</div><div><br /></div><div>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.</div><div><br /></div><div>Cheers.</div>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-37206786481785560862022-03-31T11:47:00.001-07:002022-03-31T11:47:13.429-07:00FFmpeg - Concatenating Videos with Blend Transition<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyBI0B43OMc4XU1ENO_Qpn_LxEQMDhAobpx3mIAm1N4wc912l1oU5-XMYFJkbeSfZv-mfm2gFf7q4p236HzowwAka1UMlBZ-R8rLKeVuZbUvLyPxv8A2YsNM8nRuNmM1ihZWG-wx8FPux2VvkZ3fHI1UUREf80RJac9YQigploRj7HMxgoOYnd8Xrwog/s1024/ffmpeg-logo-1024x274.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="274" data-original-width="1024" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyBI0B43OMc4XU1ENO_Qpn_LxEQMDhAobpx3mIAm1N4wc912l1oU5-XMYFJkbeSfZv-mfm2gFf7q4p236HzowwAka1UMlBZ-R8rLKeVuZbUvLyPxv8A2YsNM8nRuNmM1ihZWG-wx8FPux2VvkZ3fHI1UUREf80RJac9YQigploRj7HMxgoOYnd8Xrwog/s320/ffmpeg-logo-1024x274.png" width="320" /></a></div><br /> <p></p><p>I received an anonymous comment on a past post <a href="https://dragonquest64.blogspot.com/2019/04/ffmpeg-blending.html">FFMpeg Blending</a> specifically asking how two video files could be concatenated with a blending effect transition. </p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><i>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!</i></p></blockquote><p>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.</p><p><br /></p><h3 style="text-align: left;">Constraints/Complications</h3><h4 style="text-align: left;">Input Files</h4><p>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. </p><p>In general, your input files need to be <u>similarly formatted</u>. 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 <i>take a few minutes to check this beforehand to avoid making the mistakes I've made.</i> 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.</p><p>Let's start with <i><b>video resolution</b></i>; your input files (for concatenation and blends) need to be <u>identically</u> sized, <u>not kinda similarly sized</u>, but <u><b>identically</b></u> 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. </p><p>A consistent <i><b>fr</b></i><i style="font-weight: bold;">ame rate (FPS)</i> 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;</p><p>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 <u><b>all</b></u> have an audio track or <b><u>none</u></b> have an audio tracks. Mixing and matching will only give you headaches in the end.</p><p><br /></p><p>Example</p><p>Let's move on to our example.</p><p>Let's snag two videos from YouTube</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/aXTk9VPZ4Gg" width="320" youtube-src-id="aXTk9VPZ4Gg"></iframe></div><div class="separator" style="clear: both; text-align: center;"><span style="text-align: left;"><span style="font-family: courier; font-size: x-small;">$ youtube-dl -f mp4 -o yt-stingray.mp4 https://www.youtube.com/watch?v=aXTk9VPZ4Gg</span></span></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/_oHpWw7L3d4" width="320" youtube-src-id="_oHpWw7L3d4"></iframe></div><br /><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><span style="font-family: courier; font-size: x-small;">$ youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=_oHpWw7L3d4</span></p></blockquote><div>Examining these files, you'll find they are aren't uniform in resolution or framerate.</div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier; font-size: x-small;">$ ffprobe -i <b>yt-stingray.mp4</b> </span></div></div><div><div><span style="font-family: courier; font-size: x-small;">ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developers</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 20160609</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> configuration: --enable-libx264 --enable-nonfree --enable-gpl --enable-libfreetype --enable-libmp3lame --enable-libzmq</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavutil 56. 31.100 / 56. 31.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavcodec 58. 54.100 / 58. 54.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavformat 58. 29.100 / 58. 29.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavdevice 58. 8.100 / 58. 8.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavfilter 7. 57.100 / 7. 57.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libswscale 5. 5.100 / 5. 5.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libswresample 3. 5.100 / 3. 5.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libpostproc 55. 5.100 / 55. 5.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;">Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'yt-stingray.mp4':</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Metadata:</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> major_brand : mp42</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> minor_version : 0</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> compatible_brands: isommp42</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> creation_time : 2019-05-22T15:56:25.000000Z</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Duration: 00:47:09.44, start: 0.000000, bitrate: 397 kb/s</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, smpte170m), <b>480x360</b> [SAR 1:1 DAR 4:3], 299 kb/s, <b>23.98 fps</b>, 23.98 tbr, 24k tbn, 47.95 tbc (default)</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Metadata:</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> creation_time : 2019-05-22T15:56:25.000000Z</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> handler_name : ISO Media file produced by Google Inc. Created on: 05/22/2019.</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 95 kb/s (default)</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Metadata:</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> creation_time : 2019-05-22T15:56:25.000000Z</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> handler_name : ISO Media file produced by Google Inc. Created on: 05/22/2019.</span></div></div><div><span style="font-family: courier; font-size: x-small;"><br /></span></div><div><div><span style="font-family: courier; font-size: x-small;">$ ffprobe -i <b>yt-hardcastle01.mp4</b> </span></div></div><div><div><span style="font-family: courier; font-size: x-small;">ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developers</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 20160609</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> configuration: --enable-libx264 --enable-nonfree --enable-gpl --enable-libfreetype --enable-libmp3lame --enable-libzmq</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavutil 56. 31.100 / 56. 31.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavcodec 58. 54.100 / 58. 54.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavformat 58. 29.100 / 58. 29.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavdevice 58. 8.100 / 58. 8.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libavfilter 7. 57.100 / 7. 57.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libswscale 5. 5.100 / 5. 5.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libswresample 3. 5.100 / 3. 5.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> libpostproc 55. 5.100 / 55. 5.100</span></div></div><div><div><span style="font-family: courier; font-size: x-small;">Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'yt-hardcastle01.mp4':</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Metadata:</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> major_brand : mp42</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> minor_version : 0</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> compatible_brands: isommp42</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> encoder : Google</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Duration: 00:01:48.62, start: 0.000000, bitrate: 1362 kb/s</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), <b>1280x720</b> [SAR 1:1 DAR 16:9], 1232 kb/s, <b>29.97 fps</b>, 29.97 tbr, 30k tbn, 59.94 tbc (default)</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Metadata:</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> handler_name : ISO Media file produced by Google Inc.</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 127 kb/s (default)</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> Metadata:</span></div></div><div><div><span style="font-family: courier; font-size: x-small;"> handler_name : ISO Media file produced by Google Inc.</span></div></div></blockquote><div><br /></div><h3 style="text-align: left;">Concatenation </h3><div>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.</div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><span style="font-family: courier; font-size: x-small;">$ cat videoXX.mp4.txt</span></div><div><span style="font-family: courier; font-size: x-small;">file 'yt-stingray.mp4'</span></div><div><span style="font-family: courier; font-size: x-small;">file 'yt-hardcastle01.mp4'</span></div><div><span style="font-family: courier; font-size: x-small;"><br /></span></div><div><span style="font-family: courier; font-size: x-small;">$ ffmpeg -y -f concat -i videoXX.mp4.txt videoXX.mp4</span></div></blockquote><div><br /></div><div>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.</div><div><br /></div><div>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).</div><div><br /></div><div><br /></div><h3 style="text-align: left;">Blending</h3><div>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;</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div>[Parsed_amerge_0 @ 0x2e8b1c0] No channel layout for input 1</div></div><div><div>[Parsed_amerge_0 @ 0x2e8b1c0] Input channel layouts overlap: output layout will be determined by the number of distinct input channels</div></div><div><div>[Parsed_blend_2 @ 0x2dc7c80] First input link top parameters (size <i><b>480x360</b></i>) do not match the corresponding second input link bottom parameters (size <b><i>1280x720</i></b>)</div></div><div><div>[Parsed_blend_2 @ 0x2dc7c80] Failed to configure output pad on Parsed_blend_2</div></div><div><div>Error reinitializing filters!</div></div><div><div>Failed to inject frame into filter network: Invalid argument</div></div><div><div>Error while processing the decoded data for stream #1:0</div></div><div><div>Conversion failed!</div></div></blockquote><div><br /></div><div>This is FFmpegs way of saying that your input videos aren't uniformly sized.</div><div><br /></div><div>Scaling input files with a variety of aspect ratios is always challenging, I find <i>for me</i> scaling to a specific video size w/optional padding seems to work well for me.</div><div><br /></div><div>Let's scale our input videos to 1280x720, applying padding if necessary, and force a uniform 30/1 frame rate;</div><div><span style="font-family: courier; font-size: small;"><br /></span></div><div><span style="font-family: courier; font-size: small;">$ ffmpeg -i yt-stingray.mp4 -vf "scale=-1:720,pad=1280:ih:(ow-iw)/2" -r 30/1-strict -2 yt-stingray-scaled.mp4</span></div><div><div><span style="font-family: courier; font-size: small;">$ ffmpeg -i yt-hardcastle.mp4 -vf "scale=-1:720,pad=1280:ih:(ow-iw)/2" -r 30/1 -strict -2 yt-hardcastle-scaled.mp4</span></div><div><br /></div></div><div>We are left with uniformly 1280x720 30/1 fps videos that can be blended.</div><div><br /></div><div>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 <u>can</u> 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.</div><div><br /></div><div>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.</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier; font-size: x-small;">$ ffmpeg -i yt-stingray-scaled.mp4 -ss 4 -t 10 -strict -2 clip01-scaled.mp4</span></div></div><div><div><div><span style="font-family: courier; font-size: x-small;">$ ffmpeg -i yt-hardcastle-scaled.mp4 -ss 4 -t 10 -strict -2 clip02-scaled.mp4</span></div></div></div></blockquote><div><div><br /></div><div>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.</div><div><br /></div><div>We can achieve this by splitting the videos into 4 segments;</div><div><ul style="text-align: left;"><li>segment1.mp4 : first 8 seconds of clip01-scaled.mp4</li><li>segment2.mp4 : last 2 seconds of clip01-scaled.mp4</li><li>segment3.mp4 : first 2 seconds of clip02-scaled.mp4</li><li>segment4.mp4 : last 8 seconds of clip02-scaled.mp4</li></ul></div><div>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.</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div>$ ffmpeg -i clip01-scaled.mp4 -t 8.032000 -target ntsc-dvd -strict -2 segment1.mp4</div></div><div><div><div>$ ffmpeg -i clip01-scaled.mp4 -ss 8.032000 -target ntsc-dvd -strict -2 segment2.mp4</div></div></div><div><div>$ ffmpeg -i clip02-scaled.mp4 -ss 0 -t 2 -target ntsc-dvd -strict -2 segment3.mp4</div></div><div><div>$ ffmpeg -i clip02-scaled.mp4 -ss 2 -target ntsc-dvd -strict -2 segment4.mp4</div></div></blockquote><div><div><br /></div><div>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.</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><div style="text-align: left;"><span style="font-family: courier; font-size: x-small;">$ 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</span></div></div></blockquote><div><div><br /></div><div>It's <u>possible</u> 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.</div><div><br /></div><div>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 <u>specific attention</u> to the video duration component (e.g. 2), it <u>needs</u> 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.</div><div><br /></div><h3 style="text-align: left;">Reassembling Video Clips</h3><div>Let's reassemble our intro, outtro and blended clips into our final video.</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier; font-size: x-small;">$ cat video.mp4.txt </span></div></div><div><div><span style="font-family: courier; font-size: x-small;">file 'segment1.mp4'</span></div></div><div><div><span style="font-family: courier; font-size: x-small;">file 'segment2a.mp4'</span></div></div><div><div><span style="font-family: courier; font-size: x-small;">file 'segment4.mp4'</span></div></div><div><span style="font-family: courier; font-size: x-small;"><br /></span></div><div><span style="font-family: courier; font-size: x-small;">$ ffmpeg -y -f concat -i video.mp4.txt video.mp4</span></div></blockquote><div><br /></div><div>We're left with our final video.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dxuiBveHGPh1nHDqZ4lfWILtm1nh9fRJX1_DvHgc3uUiMxtRaPBpe43EpblPe7BmIXSNnocnWgklAJ1hjBA2w' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><br /><div><br /></div><div><br /></div><div>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.</div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><span style="font-family: courier; font-size: xx-small;"><br /></span></div><div><div><span style="font-family: courier; font-size: xx-small;">$ cat -n Makefile </span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 1<span style="white-space: pre;"> </span>TransitionDuration=2</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 2<span style="white-space: pre;"> </span>all: video.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 3<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 4<span style="white-space: pre;"> </span>segment1.mp4 : clip01-scaled.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 5<span style="white-space: pre;"> </span>${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 $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 6<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 7<span style="white-space: pre;"> </span>segment2.mp4 : clip01-scaled.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 8<span style="white-space: pre;"> </span>${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 $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 9<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 10<span style="white-space: pre;"> </span>segment3.mp4 : clip02-scaled.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 11<span style="white-space: pre;"> </span>${SH} ffmpeg -i $< -ss 0 -t $(shell echo ${TransitionDuration} -1 | bc) -target ntsc-dvd -strict -2 $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 12<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 13<span style="white-space: pre;"> </span>segment4.mp4 : clip02-scaled.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 14<span style="white-space: pre;"> </span>${SH} ffmpeg -i $< -ss ${TransitionDuration} -target ntsc-dvd -strict -2 $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 15<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 16<span style="white-space: pre;"> </span>segment2a.mp4 : segment2.mp4 segment3.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 17<span style="white-space: pre;"> </span>${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 $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 18<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 19<span style="white-space: pre;"> </span>video.mp4: segment1.mp4 segment2a.mp4 segment4.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 20<span style="white-space: pre;"> </span>${SH} rm $@.txt | true</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 21<span style="white-space: pre;"> </span>${SH} for f in $^; do echo "file '$$f'" >> $@.txt; done</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 22<span style="white-space: pre;"> </span>${SH} ffmpeg -y -f concat -i $@.txt $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 23<span style="white-space: pre;"> </span>${SH} mplayer $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 24<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 25<span style="white-space: pre;"> </span>clip01.mp4: yt-stingray.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 26<span style="white-space: pre;"> </span>${SH} ffmpeg -i $< -ss 4 -t 10 -strict -2 $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 27<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 28<span style="white-space: pre;"> </span>clip02.mp4: yt-hardcastle01.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 29<span style="white-space: pre;"> </span>${SH} ffmpeg -i $< -ss 9 -t 10 -strict -2 $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 30<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 31<span style="white-space: pre;"> </span>yt-stingray.mp4:</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 32<span style="white-space: pre;"> </span>${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=aXTk9VPZ4Gg</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 33<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 34<span style="white-space: pre;"> </span>yt-hardcastle01.mp4:</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 35<span style="white-space: pre;"> </span>${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=_oHpWw7L3d4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 36<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 37<span style="white-space: pre;"> </span>%-scaled.mp4: %.mp4</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 38<span style="white-space: pre;"> </span>${SH} ffmpeg -i $< -vf "scale=-1:720,pad=1280:ih:(ow-iw)/2" -strict -2 $@</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 39<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 40<span style="white-space: pre;"> </span></span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 41<span style="white-space: pre;"> </span>clean:</span></div></div><div><div><span style="font-family: courier; font-size: xx-small;"> 42<span style="white-space: pre;"> </span>${RM} *.mp4</span></div></div></blockquote><div><br /></div><div><br /></div><div>Cheers.</div><div><br /></div><div><br /></div><div><br /></div><div><br /></div>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-79508298414092917672022-03-21T18:30:00.000-07:002022-03-21T18:30:10.522-07:00Concatenating Non-Uniformly Scaled Videos w/FFmpeg<p> 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.</p><p><br /></p><p>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.</p><p>In past posts I've cheered the use of 'makefiles' with FFmpeg, believing they are a great match for one another; <a href="http://dragonquest64.blogspot.com/2020/09/atypical-uses-for-makefiles.html"> Atypical Uses for Makefiles </a></p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="265" src="https://www.youtube.com/embed/5fdt19VaLLY" width="319" youtube-src-id="5fdt19VaLLY"></iframe></div><p><br /></p><p>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.</p><p>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).</p><p> </p><p><span style="font-size: xx-small;"><span style="font-family: courier;"> cat -n Makefile <br /> 1 ClipW=1280<br /> 2 ClipH=720<br /> 3 Fps=30/1<br /> 4 include makefile.stingray<br /> 5 include makefile.miamivice<br /> 6 include makefile.hardcastle<br /> 7 include makefile.ateam<br /> 8 include makefile.fallguy<br /> 9 include makefile.knightrider<br /> 10 include makefile.airwolf<br /> 11 include makefile.dukes<br /> 12 include makefile.magnum<br /> 13 <br /> 14 all: video.mp4<br /> 15 <br /> 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<br /> 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<br /> 18 ${SH} rm $@.txt | true<br /> 19 ${SH} for f in $^; do echo "file '$$f'" >> $@.txt; done<br /> 20 ${SH} ffmpeg -y -f concat -i $@.txt $@<br /> 21 ${SH} mplayer $@<br /> 22 <br /> 23 <br /> 24 go: stingray-scaled.mp4<br /> 25 <br /> 26 %-scaled.mp4: %.mp4<br /> 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 $@<br /> 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 $@<br /> 29 <br /> 30 littleClean:<br /> 31 ${SH} find . -name "*.mp4" ! -name 'yt-*.mp4' -delete<br /> 32 ${RM} *.mp3<br /> 33 ${RM} *.txt<br /> 34 <br /></span></span>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.</p><p>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.</p><p><span style="font-size: xx-small;"><span style="font-family: courier;">$ cat -n makefile.stingray<br /> 1 all:<br /> 2 <br /> 3 yt-stingray.mp4:<br /> 4 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=aXTk9VPZ4Gg<br /> 5 <br /> 6 stingray.mp3: yt-stingray.mp4<br /> 7 ${SH} ffmpeg -i $< -ss 35 -t 85 -vn -strict -2 $@<br /> 8 <br /> 9 sr-clip01.mp4: yt-stingray.mp4<br /> 10 ${SH} ffmpeg -i $< -ss 4 -t 2 -strict -2 $@<br /> 11 <br /> 12 sr-clip02.mp4: yt-stingray.mp4<br /> 13 ${SH} ffmpeg -i $< -ss 26 -t 1.0 -strict -2 $@<br /> 14 <br /> 15 sr-clip03.mp4: yt-stingray.mp4<br /> 16 ${SH} ffmpeg -i $< -ss 33.30 -t 1.0 -strict -2 $@<br /> 17 <br /> 18 sr-clip04.mp4: yt-stingray.mp4<br /> 19 ${SH} ffmpeg -i $< -ss 37.00 -t 16.0 -strict -2 $@<br /> 20 <br /> 21 sr-clip05.mp4: yt-stingray.mp4<br /> 22 ${SH} ffmpeg -i $< -ss 61.50 -t 1.0 -strict -2 $@<br /> 23 <br /> 24 sr-clip06.mp4: yt-stingray.mp4<br /> 25 ${SH} ffmpeg -i $< -ss 1643.00 -t 6.0 -strict -2 $@<br /> 26 <br /> 27 sr-clip07.mp4: yt-stingray.mp4<br /> 28 ${SH} ffmpeg -i $< -ss 1874.50 -t 3.0 -strict -2 $@<br /> 29 <br /> 30 sr-clip08.mp4: yt-stingray.mp4<br /> 31 ${SH} ffmpeg -i $< -ss 2083.00 -t 6.0 -strict -2 $@<br /> 32 <br /> 33 sr-clip09.mp4: yt-stingray.mp4<br /> 34 ${SH} ffmpeg -i $< -ss 2126.50 -t 10.5 -strict -2 $@<br /> 35 <br /> 36 sr-clip10.mp4: yt-stingray.mp4<br /> 37 ${SH} ffmpeg -i $< -ss 2142.00 -t 2.0 -strict -2 $@<br /> 38 <br /> 39 sr-clip11.mp4: yt-stingray.mp4<br /> 40 ${SH} ffmpeg -i $< -ss 2149.00 -t 6.0 -strict -2 $@<br /> 41 <br /> 42 sr-clip12.mp4: yt-stingray.mp4<br /> 43 ${SH} ffmpeg -i $< -ss 2243.00 -t 1.6 -strict -2 $@<br /> 44 <br /> 45 sr-clip13.mp4: yt-stingray.mp4<br /> 46 ${SH} ffmpeg -i $< -ss 2246.00 -t 12.0 -strict -2 $@<br /> 47 <br /> 48 sr-clip14.mp4: yt-stingray.mp4<br /> 49 ${SH} ffmpeg -i $< -ss 2260.00 -t 10.0 -strict -2 $@<br /> 50 <br /> 51 sr-clip15.mp4: yt-stingray.mp4<br /> 52 ${SH} ffmpeg -i $< -ss 2278.75 -t 1.0 -strict -2 $@<br /> 53 <br /> 54 sr-clip16.mp4: yt-stingray.mp4<br /> 55 ${SH} ffmpeg -i $< -ss 2282.0 -t 2.5 -strict -2 $@<br /> 56 <br /> 57 sr-clip17.mp4: yt-stingray.mp4<br /> 58 ${SH} ffmpeg -i $< -ss 2285.5 -t 1.0 -strict -2 $@<br /> 59 <br /> 60 sr-clip18.mp4: yt-stingray.mp4<br /> 61 ${SH} ffmpeg -i $< -ss 2290.5 -t 0.25 -strict -2 $@<br /> 62 <br /> 63 sr-clip19.mp4: yt-stingray.mp4<br /> 64 ${SH} ffmpeg -i $< -ss 2293.0 -t 0.50 -strict -2 $@<br /> 65 <br /> 66 sr-clip20.mp4: yt-stingray.mp4<br /> 67 ${SH} ffmpeg -i $< -ss 2298.0 -t 1.00 -strict -2 $@<br /> 68 <br /> 69 sr-clip21.mp4: yt-stingray.mp4<br /> 70 ${SH} ffmpeg -i $< -ss 2304.0 -t 3.00 -strict -2 $@<br /> 71 <br /> 72 sr-clip22.mp4: yt-stingray.mp4<br /> 73 ${SH} ffmpeg -i $< -ss 2310.0 -t 2.00 -strict -2 $@<br /> 74 <br /> 75 sr-clip23.mp4: yt-stingray.mp4<br /> 76 ${SH} ffmpeg -i $< -ss 2317.0 -t 4.00 -strict -2 $@<br /> 77 <br /> 78 sr-clip24.mp4: yt-stingray.mp4<br /> 79 ${SH} ffmpeg -i $< -ss 2325.0 -t 2.00 -strict -2 $@<br /> 80 <br /> 81 sr-clip25.mp4: yt-stingray.mp4<br /> 82 ${SH} ffmpeg -i $< -ss 2328.5 -t 2.00 -strict -2 $@<br /> 83 <br /> 84 sr-clip26.mp4: yt-stingray.mp4<br /> 85 ${SH} ffmpeg -i $< -ss 2332.0 -t 0.50 -strict -2 $@<br /> 86 <br /> 87 sr-clip27.mp4: yt-stingray.mp4<br /> 88 ${SH} ffmpeg -i $< -ss 2334.0 -t 1.00 -strict -2 $@<br /> 89 <br /> 90 sr-clip28.mp4: yt-stingray.mp4<br /> 91 ${SH} ffmpeg -i $< -ss 2336.0 -t 1.00 -strict -2 $@<br /> 92 <br /> 93 sr-clip29.mp4: yt-stingray.mp4<br /> 94 ${SH} ffmpeg -i $< -ss 2354.0 -t 1.50 -strict -2 $@<br /> 95 <br /> 96 sr-clip30.mp4: yt-stingray.mp4<br /> 97 ${SH} ffmpeg -i $< -ss 2361.0 -t 14.00 -strict -2 $@<br /> 98 <br /> 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<br /> 100 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 101 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 102 ${RM} $@.txt<br /> 103 <br /> 104 stingray.mp4: sr-videoAn.mp4 stingray.mp3<br /> 105 ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 106 <br /> 107 clean:<br /> 108 ${RM} sr*.mp4<br />$ cat -n makefile.stingray<br /> 1 all:<br /> 2 <br /> 3 yt-stingray.mp4:<br /> 4 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=aXTk9VPZ4Gg<br /> 5 <br /> 6 stingray.mp3: yt-stingray.mp4<br /> 7 ${SH} ffmpeg -i $< -ss 35 -t 85 -vn -strict -2 $@<br /> 8 <br /> 9 sr-clip01.mp4: yt-stingray.mp4<br /> 10 ${SH} ffmpeg -i $< -ss 4 -t 2 -strict -2 $@<br /> 11 <br /> 12 sr-clip02.mp4: yt-stingray.mp4<br /> 13 ${SH} ffmpeg -i $< -ss 26 -t 1.0 -strict -2 $@<br /> 14 <br /> 15 sr-clip03.mp4: yt-stingray.mp4<br /> 16 ${SH} ffmpeg -i $< -ss 33.30 -t 1.0 -strict -2 $@<br /> 17 <br /> 18 sr-clip04.mp4: yt-stingray.mp4<br /> 19 ${SH} ffmpeg -i $< -ss 37.00 -t 16.0 -strict -2 $@<br /> 20 <br /> 21 sr-clip05.mp4: yt-stingray.mp4<br /> 22 ${SH} ffmpeg -i $< -ss 61.50 -t 1.0 -strict -2 $@<br /> 23 <br /> 24 sr-clip06.mp4: yt-stingray.mp4<br /> 25 ${SH} ffmpeg -i $< -ss 1643.00 -t 6.0 -strict -2 $@<br /> 26 <br /> 27 sr-clip07.mp4: yt-stingray.mp4<br /> 28 ${SH} ffmpeg -i $< -ss 1874.50 -t 3.0 -strict -2 $@<br /> 29 <br /> 30 sr-clip08.mp4: yt-stingray.mp4<br /> 31 ${SH} ffmpeg -i $< -ss 2083.00 -t 6.0 -strict -2 $@<br /> 32 <br /> 33 sr-clip09.mp4: yt-stingray.mp4<br /> 34 ${SH} ffmpeg -i $< -ss 2126.50 -t 10.5 -strict -2 $@<br /> 35 <br /> 36 sr-clip10.mp4: yt-stingray.mp4<br /> 37 ${SH} ffmpeg -i $< -ss 2142.00 -t 2.0 -strict -2 $@<br /> 38 <br /> 39 sr-clip11.mp4: yt-stingray.mp4<br /> 40 ${SH} ffmpeg -i $< -ss 2149.00 -t 6.0 -strict -2 $@<br /> 41 <br /> 42 sr-clip12.mp4: yt-stingray.mp4<br /> 43 ${SH} ffmpeg -i $< -ss 2243.00 -t 1.6 -strict -2 $@<br /> 44 <br /> 45 sr-clip13.mp4: yt-stingray.mp4<br /> 46 ${SH} ffmpeg -i $< -ss 2246.00 -t 12.0 -strict -2 $@<br /> 47 <br /> 48 sr-clip14.mp4: yt-stingray.mp4<br /> 49 ${SH} ffmpeg -i $< -ss 2260.00 -t 10.0 -strict -2 $@<br /> 50 <br /> 51 sr-clip15.mp4: yt-stingray.mp4<br /> 52 ${SH} ffmpeg -i $< -ss 2278.75 -t 1.0 -strict -2 $@<br /> 53 <br /> 54 sr-clip16.mp4: yt-stingray.mp4<br /> 55 ${SH} ffmpeg -i $< -ss 2282.0 -t 2.5 -strict -2 $@<br /> 56 <br /> 57 sr-clip17.mp4: yt-stingray.mp4<br /> 58 ${SH} ffmpeg -i $< -ss 2285.5 -t 1.0 -strict -2 $@<br /> 59 <br /> 60 sr-clip18.mp4: yt-stingray.mp4<br /> 61 ${SH} ffmpeg -i $< -ss 2290.5 -t 0.25 -strict -2 $@<br /> 62 <br /> 63 sr-clip19.mp4: yt-stingray.mp4<br /> 64 ${SH} ffmpeg -i $< -ss 2293.0 -t 0.50 -strict -2 $@<br /> 65 <br /> 66 sr-clip20.mp4: yt-stingray.mp4<br /> 67 ${SH} ffmpeg -i $< -ss 2298.0 -t 1.00 -strict -2 $@<br /> 68 <br /> 69 sr-clip21.mp4: yt-stingray.mp4<br /> 70 ${SH} ffmpeg -i $< -ss 2304.0 -t 3.00 -strict -2 $@<br /> 71 <br /> 72 sr-clip22.mp4: yt-stingray.mp4<br /> 73 ${SH} ffmpeg -i $< -ss 2310.0 -t 2.00 -strict -2 $@<br /> 74 <br /> 75 sr-clip23.mp4: yt-stingray.mp4<br /> 76 ${SH} ffmpeg -i $< -ss 2317.0 -t 4.00 -strict -2 $@<br /> 77 <br /> 78 sr-clip24.mp4: yt-stingray.mp4<br /> 79 ${SH} ffmpeg -i $< -ss 2325.0 -t 2.00 -strict -2 $@<br /> 80 <br /> 81 sr-clip25.mp4: yt-stingray.mp4<br /> 82 ${SH} ffmpeg -i $< -ss 2328.5 -t 2.00 -strict -2 $@<br /> 83 <br /> 84 sr-clip26.mp4: yt-stingray.mp4<br /> 85 ${SH} ffmpeg -i $< -ss 2332.0 -t 0.50 -strict -2 $@<br /> 86 <br /> 87 sr-clip27.mp4: yt-stingray.mp4<br /> 88 ${SH} ffmpeg -i $< -ss 2334.0 -t 1.00 -strict -2 $@<br /> 89 <br /> 90 sr-clip28.mp4: yt-stingray.mp4<br /> 91 ${SH} ffmpeg -i $< -ss 2336.0 -t 1.00 -strict -2 $@<br /> 92 <br /> 93 sr-clip29.mp4: yt-stingray.mp4<br /> 94 ${SH} ffmpeg -i $< -ss 2354.0 -t 1.50 -strict -2 $@<br /> 95 <br /> 96 sr-clip30.mp4: yt-stingray.mp4<br /> 97 ${SH} ffmpeg -i $< -ss 2361.0 -t 14.00 -strict -2 $@<br /> 98 <br /> 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<br /> 100 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 101 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 102 ${RM} $@.txt<br /> 103 <br /> 104 stingray.mp4: sr-videoAn.mp4 stingray.mp3<br /> 105 ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 106 <br /> 107 clean:<br /> 108 ${RM} sr*.mp4<br />$ cat -n makefile.miamivice<br /> 1 yt-miamiVice01.mp4:<br /> 2 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=JmFyFqTJprg<br /> 3 <br /> 4 yt-miamiVice02.mp4:<br /> 5 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=dEjXPY9jOx8<br /> 6 <br /> 7 miamiVice.mp3: yt-miamiVice02.mp4<br /> 8 ${SH} ffmpeg -i $< -vn $@<br /> 9 <br /> 10 #56-78<br /> 11 mv-clip01.mp4: yt-miamiVice01.mp4<br /> 12 ${SH} ffmpeg -i $< -ss 56 -t 22 $@<br /> 13 ${SH} mplayer $@<br /> 14 <br /> 15 #84.5-87<br /> 16 mv-clip02.mp4: yt-miamiVice01.mp4<br /> 17 ${SH} ffmpeg -i $< -ss 84.5 -t 2.5 $@<br /> 18 ${SH} mplayer $@<br /> 19 <br /> 20 #114-118<br /> 21 mv-clip03.mp4: yt-miamiVice01.mp4<br /> 22 ${SH} ffmpeg -i $< -ss 114 -t 4 $@<br /> 23 ${SH} mplayer $@<br /> 24 <br /> 25 #122-127<br /> 26 mv-clip04.mp4: yt-miamiVice01.mp4<br /> 27 ${SH} ffmpeg -i $< -ss 122 -t 5 $@<br /> 28 ${SH} mplayer $@<br /> 29 <br /> 30 #131-142<br /> 31 mv-clip05.mp4: yt-miamiVice01.mp4<br /> 32 ${SH} ffmpeg -i $< -ss 131 -t 11 $@<br /> 33 ${SH} mplayer $@<br /> 34 <br /> 35 #153-160<br /> 36 mv-clip06.mp4: yt-miamiVice01.mp4<br /> 37 ${SH} ffmpeg -i $< -ss 154.5 -t 7 $@<br /> 38 ${SH} mplayer $@<br /> 39 <br /> 40 #169-174<br /> 41 mv-clip07.mp4: yt-miamiVice01.mp4<br /> 42 ${SH} ffmpeg -i $< -ss 169 -t 5 $@<br /> 43 ${SH} mplayer $@<br /> 44 <br /> 45 #183-190<br /> 46 mv-clip08.mp4: yt-miamiVice01.mp4<br /> 47 ${SH} ffmpeg -i $< -ss 183 -t 7 $@<br /> 48 ${SH} mplayer $@<br /> 49 <br /> 50 #193-194<br /> 51 mv-clip09.mp4: yt-miamiVice01.mp4<br /> 52 ${SH} ffmpeg -i $< -ss 193 -t 1 $@<br /> 53 ${SH} mplayer $@<br /> 54 <br /> 55 #198-204<br /> 56 mv-clip10.mp4: yt-miamiVice01.mp4<br /> 57 ${SH} ffmpeg -i $< -ss 198 -t 5 $@<br /> 58 ${SH} mplayer $@<br /> 59 <br /> 60 #254-265<br /> 61 mv-clip11.mp4: yt-miamiVice01.mp4<br /> 62 ${SH} ffmpeg -i $< -ss 254 -t 11 $@<br /> 63 ${SH} mplayer $@<br /> 64 <br /> 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 <br /> 66 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 67 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 68 ${RM} $@.txt<br /> 69 <br /> 70 miamiVice.mp4: mv-videoAn.mp4 miamiVice.mp3<br /> 71 ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 72 <br /> 73 <br />$ cat -n makefile.hardcastle<br /> 1 yt-hardcastle01.mp4:<br /> 2 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=_oHpWw7L3d4<br /> 3 <br /> 4 <br /> 5 yt-hardcastle02.mp4:<br /> 6 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=J2_G1CNaeFs<br /> 7 <br /> 8 hardcastle.mp3: yt-hardcastle01.mp4<br /> 9 ${SH} ffmpeg -i $< -ss 8.3 -t 91.6 -vn -strict -2 $@<br /> 10 <br /> 11 hm-clip01.mp4: yt-hardcastle01.mp4<br /> 12 ${SH} ffmpeg -i $< -ss 8.3 -t 16.00 -strict -2 $@<br /> 13 <br /> 14 hm-clip02.mp4: yt-hardcastle01.mp4<br /> 15 ${SH} ffmpeg -i $< -ss 82.3 -t 0.5 -strict -2 $@<br /> 16 <br /> 17 hm-clip03.mp4: yt-hardcastle01.mp4<br /> 18 ${SH} ffmpeg -i $< -ss 89.5 -t 12.0 -strict -2 $@<br /> 19 <br /> 20 hm-clip04.mp4: yt-hardcastle02.mp4<br /> 21 ${SH} ffmpeg -i $< -ss 31.5 -t 3.5 -strict -2 $@<br /> 22 <br /> 23 hm-clip05.mp4: yt-hardcastle02.mp4<br /> 24 ${SH} ffmpeg -i $< -ss 40.0 -t 3.0 -strict -2 $@<br /> 25 <br /> 26 hm-clip06.mp4: yt-hardcastle02.mp4<br /> 27 ${SH} ffmpeg -i $< -ss 51.0 -t 2.0 -strict -2 $@<br /> 28 <br /> 29 hm-clip07.mp4: yt-hardcastle02.mp4<br /> 30 ${SH} ffmpeg -i $< -ss 68.5 -t 7.0 -strict -2 $@<br /> 31 <br /> 32 hm-clip08.mp4: yt-hardcastle02.mp4<br /> 33 ${SH} ffmpeg -i $< -ss 84.5 -t 25.5 -strict -2 $@<br /> 34 <br /> 35 hm-clip09.mp4: yt-hardcastle02.mp4<br /> 36 ${SH} ffmpeg -i $< -ss 116.0 -t 8.0 -strict -2 $@<br /> 37 <br /> 38 hm-clip10.mp4: yt-hardcastle02.mp4<br /> 39 ${SH} ffmpeg -i $< -ss 133.0 -t 5.0 -strict -2 $@<br /> 40 <br /> 41 hm-clip11.mp4: yt-hardcastle02.mp4<br /> 42 ${SH} ffmpeg -i $< -ss 143.5 -t 5.0 -strict -2 $@<br /> 43 <br /> 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<br /> 45 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 46 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 47 ${RM} $@.txt<br /> 48 <br /> 49 hardcastle.mp4: hm-videoAn.mp4 hardcastle.mp3<br /> 50 ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 51 <br /> 52 <br /> 53 <br />$ cat -n makefile.ateam<br /> 1 yt-aTeam00.mp4:<br /> 2 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=wyz_2DEah4o<br /> 3 <br /> 4 yt-aTeam01.mp4:<br /> 5 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=orxNlmQSMg8<br /> 6 <br /> 7 yt-aTeam02.mp4:<br /> 8 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=nn5gY7LK8Mc<br /> 9 <br /> 10 yt-aTeam03.mp4:<br /> 11 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=ZgUBogpF-M8<br /> 12 <br /> 13 yt-aTeam04.mp4:<br /> 14 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=TP0LowFsi9Q<br /> 15 <br /> 16 yt-aTeam05.mp4:<br /> 17 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=7BcPko1RYv0<br /> 18 <br /> 19 at-clip01.mp4: yt-aTeam01.mp4<br /> 20 ${SH} ffmpeg -i $< -ss 0 -t 10 -strict -2 $@<br /> 21 <br /> 22 at-clip02.mp4: yt-aTeam01.mp4<br /> 23 ${SH} ffmpeg -i $< -ss 13 -t 9 -strict -2 $@<br /> 24 <br /> 25 at-clip03.mp4: yt-aTeam02.mp4<br /> 26 ${SH} ffmpeg -i $< -ss 1.3 -t 2.0 -strict -2 $@<br /> 27 <br /> 28 at-clip04.mp4: yt-aTeam02.mp4<br /> 29 ${SH} ffmpeg -i $< -ss 5.5 -t 0.25 -strict -2 $@<br /> 30 <br /> 31 at-clip05.mp4: yt-aTeam02.mp4<br /> 32 ${SH} ffmpeg -i $< -ss 9.5 -t 5 -strict -2 $@<br /> 33 <br /> 34 at-clip06.mp4: yt-aTeam02.mp4<br /> 35 ${SH} ffmpeg -i $< -ss 30 -t 2.5 -strict -2 $@<br /> 36 <br /> 37 at-clip07.mp4: yt-aTeam02.mp4<br /> 38 ${SH} ffmpeg -i $< -ss 34.5 -t 1 -strict -2 $@<br /> 39 <br /> 40 at-clip08.mp4: yt-aTeam02.mp4<br /> 41 ${SH} ffmpeg -i $< -ss 40 -t 2 -strict -2 $@<br /> 42 <br /> 43 at-clip09.mp4: yt-aTeam02.mp4<br /> 44 ${SH} ffmpeg -i $< -ss 50 -t 3.5 -strict -2 $@<br /> 45 <br /> 46 at-clip10.mp4: yt-aTeam02.mp4<br /> 47 ${SH} ffmpeg -i $< -ss 64 -t 5 -strict -2 $@<br /> 48 <br /> 49 at-clip11.mp4: yt-aTeam02.mp4<br /> 50 ${SH} ffmpeg -i $< -ss 75.5 -t 1.5 -strict -2 $@<br /> 51 <br /> 52 at-clip12.mp4: yt-aTeam02.mp4<br /> 53 ${SH} ffmpeg -i $< -ss 79 -t 7 -strict -2 $@<br /> 54 <br /> 55 at-clip13.mp4: yt-aTeam03.mp4<br /> 56 ${SH} ffmpeg -i $< -ss 311 -t 7 -vf scale=486:360 -r 25/1 -strict -2 $@<br /> 57 <br /> 58 at-clip14.mp4: yt-aTeam04.mp4<br /> 59 ${SH} ffmpeg -i $< -ss 218 -t 3 -vf scale=486:360 -r 25/1 -strict -2 $@<br /> 60 <br /> 61 at-clip15.mp4: yt-aTeam04.mp4<br /> 62 ${SH} ffmpeg -i $< -ss 224 -t 1 -vf scale=486:360 -r 25/1 -strict -2 $@<br /> 63 <br /> 64 at-clip16.mp4: yt-aTeam04.mp4<br /> 65 ${SH} ffmpeg -i $< -ss 228 -t 2 -vf scale=486:360 -r 25/1 -strict -2 $@<br /> 66 <br /> 67 at-clip17.mp4: yt-aTeam04.mp4<br /> 68 ${SH} ffmpeg -i $< -ss 238 -t 1 -vf scale=486:360 -r 25/1 -strict -2 $@<br /> 69 <br /> 70 at-clip18.mp4: yt-aTeam04.mp4<br /> 71 ${SH} ffmpeg -i $< -ss 243 -t 5 -vf scale=486:360 -r 25/1 -strict -2 $@<br /> 72 <br /> 73 at-clip19.mp4: yt-aTeam05.mp4<br /> 74 ${SH} ffmpeg -i $< -ss 112 -t 5 -vf scale=486:360 -r 25/1 -strict -2 $@<br /> 75 <br /> 76 at-clip20.mp4: yt-aTeam02.mp4<br /> 77 ${SH} ffmpeg -y -i $< -ss 81 -t 5 -strict -2 temp.$@<br /> 78 ${SH} ffmpeg -i temp.$@ -filter:v "setpts=2.5*PTS" -strict -2 $@<br /> 79 <br /> 80 aTeam.mp3: yt-aTeam00.mp4<br /> 81 ${SH} ffmpeg -y -i $< -vn -t 75 -strict -2 $@<br /> 82 <br /> 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<br /> 84 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 85 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 86 ${RM} $@.txt<br /> 87 <br /> 88 aTeam.mp4: at-videoAn.mp4 aTeam.mp3<br /> 89 ${SH} ffmpeg -y -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 90 <br />$ cat -n makefile.fallguy<br /> 1 yt-fallguy01.mp4:<br /> 2 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=iNizcrfnQ4E<br /> 3 <br /> 4 yt-fallguy02.mp4:<br /> 5 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=0dxtiOqK8qg<br /> 6 <br /> 7 yt-fallguy03.mp4:<br /> 8 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=dj9ev40C30M<br /> 9 <br /> 10 yt-fallguy04.mp4:<br /> 11 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=s1uhbxz_Xfc<br /> 12 <br /> 13 yt-fallguy05.mp4:<br /> 14 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=atZtkZdIWeM<br /> 15 <br /> 16 fg-clip01.mp4: yt-fallguy01.mp4<br /> 17 ${SH} ffmpeg -i $< -ss 0 -t 4 -strict -2 $@<br /> 18 <br /> 19 fg-clip02.mp4: yt-fallguy02.mp4<br /> 20 ${SH} ffmpeg -i $< -ss 61 -t 3 -strict -2 $@<br /> 21 <br /> 22 fg-clip03.mp4: yt-fallguy02.mp4<br /> 23 ${SH} ffmpeg -i $< -ss 81 -t 3 -strict -2 $@<br /> 24 <br /> 25 fg-clip04.mp4: yt-fallguy02.mp4<br /> 26 ${SH} ffmpeg -i $< -ss 101 -t 3 -strict -2 $@<br /> 27 <br /> 28 fg-clip05.mp4: yt-fallguy02.mp4<br /> 29 ${SH} ffmpeg -i $< -ss 217 -t 5 -strict -2 $@<br /> 30 <br /> 31 fg-clip06.mp4: yt-fallguy02.mp4<br /> 32 ${SH} ffmpeg -i $< -ss 248 -t 5 -strict -2 $@<br /> 33 <br /> 34 fg-clip07.mp4: yt-fallguy02.mp4<br /> 35 ${SH} ffmpeg -i $< -ss 289 -t 3 -strict -2 $@<br /> 36 <br /> 37 fg-clip08.mp4: yt-fallguy02.mp4<br /> 38 ${SH} ffmpeg -i $< -ss 5 -t 9 -strict -2 $@<br /> 39 <br /> 40 fg-clip09.mp4: yt-fallguy02.mp4<br /> 41 ${SH} ffmpeg -i $< -ss 17 -t 9 -strict -2 $@<br /> 42 <br /> 43 fg-clip10.mp4: yt-fallguy02.mp4<br /> 44 ${SH} ffmpeg -i $< -ss 28.5 -t 5.5 -strict -2 $@<br /> 45 <br /> 46 fg-clip11.mp4: yt-fallguy02.mp4<br /> 47 ${SH} ffmpeg -i $< -ss 38.5 -t 1.5 -strict -2 $@<br /> 48 <br /> 49 fg-clip12.mp4: yt-fallguy02.mp4<br /> 50 ${SH} ffmpeg -i $< -ss 42 -t 2.5 -strict -2 $@<br /> 51 <br /> 52 fg-clip13.mp4: yt-fallguy02.mp4<br /> 53 ${SH} ffmpeg -i $< -ss 46.5 -t 5.5 -strict -2 $@<br /> 54 <br /> 55 fg-clip14.mp4: yt-fallguy02.mp4<br /> 56 ${SH} ffmpeg -i $< -ss 54 -t 6 -strict -2 $@<br /> 57 <br /> 58 fg-clip15.mp4: yt-fallguy02.mp4<br /> 59 ${SH} ffmpeg -i $< -ss 62 -t 3 -strict -2 $@<br /> 60 <br /> 61 fg-clip16.mp4: yt-fallguy02.mp4<br /> 62 ${SH} ffmpeg -i $< -ss 66 -t 36 -strict -2 $@<br /> 63 <br /> 64 fg-clip17.mp4: yt-fallguy02.mp4<br /> 65 ${SH} ffmpeg -i $< -ss 106 -t 24 -strict -2 $@<br /> 66 <br /> 67 fg-clip18.mp4: yt-fallguy02.mp4<br /> 68 ${SH} ffmpeg -i $< -ss 137 -t 12 -strict -2 $@<br /> 69 <br /> 70 fg-clip19.mp4: yt-fallguy02.mp4<br /> 71 ${SH} ffmpeg -i $< -ss 151 -t 19 -strict -2 $@<br /> 72 <br /> 73 fg-clip20.mp4: yt-fallguy02.mp4<br /> 74 ${SH} ffmpeg -i $< -ss 178 -t 2 -strict -2 $@<br /> 75 <br /> 76 fg-clip21.mp4: yt-fallguy02.mp4<br /> 77 # ${SH} ffmpeg -i $< -ss 188 -t 222 -strict -2 $@<br /> 78 # ${SH} ffmpeg -i $< -ss 188 -t 165 -strict -2 $@<br /> 79 ${SH} ffmpeg -i $< -ss 188 -t 23 -strict -2 $@<br /> 80 <br /> 81 fallGuy.mp3: yt-fallguy01.mp4<br /> 82 ${SH} ffmpeg -i $< -ss 0 -t 106 -vn -strict -2 $@<br /> 83 <br /> 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<br /> 85 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 86 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 87 ${RM} $@.txt<br /> 88 <br /> 89 fallGuy.mp4: fg-videoAn.mp4 fallGuy.mp3<br /> 90 ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 91 <br />$ cat -n makefile.knightrider<br /> 1 yt-knightrider01.mp4:<br /> 2 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=oNyXYPhnUIs<br /> 3 <br /> 4 yt-knightrider02.mp4:<br /> 5 ${SH} youtube-dl -f mp4 -o $@ https://youtu.be/hfRiedxPQhs<br /> 6 <br /> 7 yt-knightrider03.mp4:<br /> 8 ${SH} youtube-dl -f mp4 -o $@ https://youtu.be/LcG5jQPrOOQ<br /> 9 <br /> 10 yt-knightrider04.mp4:<br /> 11 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=FBQ0PSxPRjc<br /> 12 <br /> 13 yt-knightrider05.mp4:<br /> 14 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=rkBcGP256Ng<br /> 15 <br /> 16 yt-knightrider06.mp4:<br /> 17 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=lVElVZlnTG0<br /> 18 <br /> 19 yt-knightrider07.mp4:<br /> 20 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=mWDZsKSGoVg<br /> 21 <br /> 22 kr-clip01.mp4: yt-knightrider01.mp4<br /> 23 ${SH} ffmpeg -i $< -ss 4 -t 27 -target ntsc-dvd $@<br /> 24 <br /> 25 kr-clip02.mp4: yt-knightrider01.mp4<br /> 26 ${SH} ffmpeg -i $< -ss 53 -t 7 -target ntsc-dvd $@<br /> 27 <br /> 28 kr-clip03.mp4: yt-knightrider01.mp4<br /> 29 ${SH} ffmpeg -i $< -ss 65 -t 0.5 -target ntsc-dvd $@<br /> 30 <br /> 31 #kr-clip04.mp4: yt-knightrider01.mp4<br /> 32 # ${SH} ffmpeg -i $< -ss 67 -t 10 -target ntsc-dvd $@<br /> 33 <br /> 34 <br /> 35 knightRider.mp3: yt-knightrider01.mp4<br /> 36 ${SH} ffmpeg -i $< -ss 4 -t 76 -vn $@<br /> 37 <br /> 38 kr-clip04.mp4: yt-knightrider02.mp4<br /> 39 ${SH} ffmpeg -i $< -ss 285 -t 1 -target ntsc-dvd $@<br /> 40 <br /> 41 kr-clip05.mp4: yt-knightrider02.mp4<br /> 42 ${SH} ffmpeg -i $< -ss 295 -t 4 -target ntsc-dvd $@<br /> 43 <br /> 44 kr-clip06.mp4: yt-knightrider02.mp4<br /> 45 ${SH} ffmpeg -i $< -ss 315 -t 1 -target ntsc-dvd $@<br /> 46 <br /> 47 kr-clip07.mp4: yt-knightrider02.mp4<br /> 48 ${SH} ffmpeg -i $< -ss 330 -t 10 -target ntsc-dvd $@<br /> 49 <br /> 50 kr-clip08.mp4: yt-knightrider02.mp4<br /> 51 ${SH} ffmpeg -i $< -ss 347 -t 3 -target ntsc-dvd $@<br /> 52 <br /> 53 kr-clip09.mp4: yt-knightrider02.mp4<br /> 54 ${SH} ffmpeg -i $< -ss 352 -t 5 -target ntsc-dvd $@<br /> 55 <br /> 56 kr-clip10.mp4: yt-knightrider02.mp4<br /> 57 ${SH} ffmpeg -i $< -ss 362 -t 5 -target ntsc-dvd $@<br /> 58 <br /> 59 kr-clip11.mp4: yt-knightrider01.mp4<br /> 60 ${SH} ffmpeg -i $< -ss 67 -t 10 -target ntsc-dvd $@<br /> 61 <br /> 62 kr-clip12.mp4: yt-knightrider06.mp4<br /> 63 ${SH} ffmpeg -i $< -ss 4.2 -t 10 -target ntsc-dvd $@<br /> 64 <br /> 65 <br /> 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<br /> 67 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 68 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 69 ${RM} $@.txt<br /> 70 ${SH} mplayer $@<br /> 71 <br /> 72 <br /> 73 knightRider.mp4: kr-videoAn.mp4 knightRider.mp3<br /> 74 ${SH} ffmpeg -y -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 75 <br /> 76 <br />$ cat -n makefile.airwolf<br /> 1 yt-airwolf01.mp4:<br /> 2 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=B10AXxnZGto<br /> 3 <br /> 4 yt-airwolf02.mp4:<br /> 5 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=55VuB724n6Y<br /> 6 <br /> 7 yt-airwolf03.mp4:<br /> 8 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=dnHY4rhNRKU<br /> 9 <br /> 10 airwolf.mp3: yt-airwolf01.mp4<br /> 11 ${SH} ffmpeg -i $< -vn -strict -2 $@<br /> 12 <br /> 13 <br /> 14 aw-clip01.mp4: yt-airwolf01.mp4<br /> 15 ${SH} ffmpeg -i $< -ss 1 -t 39 -strict -2 -target ntsc-dvd $@<br /> 16 <br /> 17 aw-clip02.mp4: yt-airwolf01.mp4<br /> 18 ${SH} ffmpeg -i $< -ss 74 -t 6 -strict -2 -target ntsc-dvd $@<br /> 19 <br /> 20 aw-clip03.mp4: yt-airwolf02.mp4<br /> 21 ${SH} ffmpeg -i $< -ss 28 -t 12 -strict -2 -target ntsc-dvd $@<br /> 22 <br /> 23 aw-clip04.mp4: yt-airwolf02.mp4<br /> 24 # ${SH} ffmpeg -i $< -ss 130 -t 50 -strict -2 -target ntsc-dvd $@<br /> 25 ${SH} ffmpeg -i $< -ss 130 -t 48 -strict -2 -target ntsc-dvd $@<br /> 26 <br /> 27 aw-clip05.mp4: yt-airwolf02.mp4<br /> 28 ${SH} ffmpeg -i $< -ss 201 -t 2 -strict -2 -target ntsc-dvd $@<br /> 29 <br /> 30 aw-clip06.mp4: yt-airwolf02.mp4<br /> 31 ${SH} ffmpeg -i $< -ss 209 -t 19 -strict -2 -target ntsc-dvd $@<br /> 32 <br /> 33 aw-clip07.mp4: yt-airwolf02.mp4<br /> 34 ${SH} ffmpeg -i $< -ss 240 -t 4 -strict -2 -target ntsc-dvd $@<br /> 35 <br /> 36 aw-clip08.mp4: yt-airwolf02.mp4<br /> 37 ${SH} ffmpeg -i $< -ss 254 -t 4 -strict -2 -target ntsc-dvd $@<br /> 38 <br /> 39 aw-clip09.mp4: yt-airwolf02.mp4<br /> 40 ${SH} ffmpeg -i $< -ss 265 -t 3 -strict -2 -target ntsc-dvd $@<br /> 41 <br /> 42 aw-clip10.mp4: yt-airwolf02.mp4<br /> 43 ${SH} ffmpeg -i $< -ss 550 -t 10 -strict -2 -target ntsc-dvd $@<br /> 44 <br /> 45 aw-clip11.mp4: yt-airwolf02.mp4<br /> 46 ${SH} ffmpeg -i $< -ss 735 -t 40 -strict -2 -target ntsc-dvd $@<br /> 47 <br /> 48 aw-videoAn.mp4: aw-clip01.mp4 aw-clip04.mp4 <br /> 49 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 50 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 51 ${RM} $@.txt<br /> 52 <br /> 53 airwolf.mp4: aw-videoAn.mp4 airwolf.mp3<br /> 54 ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 55 <br />$ cat -n makefile.dukes<br /> 1 yt-dukes01.mp4:<br /> 2 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=67gig0f4HLo<br /> 3 <br /> 4 yt-dukes02.mp4:<br /> 5 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=2iMBQ-bXQIE<br /> 6 <br /> 7 yt-dukes03.mp4:<br /> 8 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=pWuVWNSNWnM<br /> 9 <br /> 10 dukes.mp3: yt-dukes01.mp4<br /> 11 ${SH} ffmpeg -i $< -vn -t 61 -strict -2 $@<br /> 12 <br /> 13 dukes.mp4: dh-videoAn.mp4 dukes.mp3<br /> 14 ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 15 <br /> 16 dh-clip01.mp4: yt-dukes01.mp4<br /> 17 ${SH} ffmpeg -i $< -ss 3 -t 7 -strict -2 $@<br /> 18 ${SH} mplayer $@<br /> 19 <br /> 20 dh-clip02.mp4: yt-dukes01.mp4<br /> 21 ${SH} ffmpeg -i $< -ss 15 -t 1.5 -strict -2 $@<br /> 22 ${SH} mplayer $@<br /> 23 <br /> 24 dh-clip03.mp4: yt-dukes01.mp4<br /> 25 ${SH} ffmpeg -i $< -ss 52.5 -t 8 -strict -2 $@<br /> 26 ${SH} mplayer $@<br /> 27 <br /> 28 dh-clip04.mp4: yt-dukes02.mp4<br /> 29 ${SH} ffmpeg -i $< -ss 94.5 -t 4 -strict -2 $@<br /> 30 ${SH} mplayer $@<br /> 31 <br /> 32 #dh-clip05.mp4: yt-dukes02.mp4<br /> 33 # ${SH} ffmpeg -i $< -ss 177.5 -t 4.5 -strict -2 $@<br /> 34 # ${SH} mplayer $@<br /> 35 <br /> 36 dh-clip06.mp4: yt-dukes02.mp4<br /> 37 ${SH} ffmpeg -i $< -ss 212 -t 3.5 -strict -2 $@<br /> 38 ${SH} mplayer $@<br /> 39 <br /> 40 dh-clip07.mp4: yt-dukes02.mp4<br /> 41 ${SH} ffmpeg -i $< -ss 561.5 -t 10 -strict -2 $@<br /> 42 ${SH} mplayer $@<br /> 43 <br /> 44 dh-clip08.mp4: yt-dukes03.mp4<br /> 45 ${SH} ffmpeg -i $< -ss 18 -t 3.25 -strict -2 $@<br /> 46 ${SH} mplayer $@<br /> 47 <br /> 48 dh-clip09.mp4: yt-dukes03.mp4<br /> 49 ${SH} ffmpeg -i $< -ss 24 -t 2.5 -strict -2 $@<br /> 50 ${SH} mplayer $@<br /> 51 <br /> 52 #dh-clip10.mp4: yt-dukes03.mp4<br /> 53 # ${SH} ffmpeg -i $< -ss 29 -t 13 -strict -2 $@<br /> 54 # ${SH} mplayer $@<br /> 55 <br /> 56 dh-clip11.mp4: yt-dukes03.mp4<br /> 57 ${SH} ffmpeg -i $< -ss 68 -t 5.5 -strict -2 $@<br /> 58 ${SH} mplayer $@<br /> 59 <br /> 60 dh-clip12.mp4: yt-dukes03.mp4<br /> 61 ${SH} ffmpeg -i $< -ss 78 -t 13 -strict -2 $@<br /> 62 ${SH} mplayer $@<br /> 63 <br /> 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<br /> 65 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 66 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 67 ${RM} $@.txt<br /> 68 <br />$ cat -n makefile.magnum<br /> 1 yt-magnum01.mp4:<br /> 2 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=xIaXl7SqkBw<br /> 3 <br /> 4 yt-magnum02.mp4:<br /> 5 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=CICbxj1PF74<br /> 6 <br /> 7 magnum.mp3: yt-magnum01.mp4<br /> 8 ${SH} ffmpeg -i $< -vn -t 61 -strict -2 $@<br /> 9 <br /> 10 magnum.mp4: mp-videoAn.mp4 magnum.mp3<br /> 11 ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@<br /> 12 <br /> 13 mp-clip01.mp4: yt-magnum01.mp4<br /> 14 ${SH} ffmpeg -i $< -ss 16 -t 4 -strict -2 $@<br /> 15 <br /> 16 mp-clip02.mp4: yt-magnum01.mp4<br /> 17 ${SH} ffmpeg -i $< -ss 40.5 -t 1.5 -strict -2 $@<br /> 18 <br /> 19 #mp-clip03.mp4: yt-magnum02.mp4<br /> 20 # ${SH} ffmpeg -i $< -ss 0 -t 22 -strict -2 $@<br /> 21 <br /> 22 mp-clip04.mp4: yt-magnum02.mp4<br /> 23 ${SH} ffmpeg -i $< -ss 24 -t 6 -strict -2 $@<br /> 24 <br /> 25 mp-clip05.mp4: yt-magnum02.mp4<br /> 26 ${SH} ffmpeg -i $< -ss 32.5 -t 1 -strict -2 $@<br /> 27 <br /> 28 #mp-clip06.mp4: yt-magnum02.mp4<br /> 29 # ${SH} ffmpeg -i $< -ss 38.5 -t 2.5 -strict -2 $@<br /> 30 #<br /> 31 #mp-clip07.mp4: yt-magnum02.mp4<br /> 32 # ${SH} ffmpeg -i $< -ss 47 -t 2.5 -strict -2 $@<br /> 33 <br /> 34 #mp-clip08.mp4: yt-magnum02.mp4<br /> 35 # ${SH} ffmpeg -i $< -ss 58 -t 17 -strict -2 $@<br /> 36 #<br /> 37 #mp-clip09.mp4: yt-magnum02.mp4<br /> 38 # ${SH} ffmpeg -i $< -ss 77.5 -t 15 -strict -2 $@<br /> 39 <br /> 40 mp-clip10.mp4: yt-magnum02.mp4<br /> 41 ${SH} ffmpeg -i $< -ss 101 -t 20 -strict -2 $@<br /> 42 <br /> 43 mp-clip11.mp4: yt-magnum02.mp4<br /> 44 ${SH} ffmpeg -i $< -ss 123 -t 3 -strict -2 $@<br /> 45 <br /> 46 mp-clip12.mp4: yt-magnum02.mp4<br /> 47 ${SH} ffmpeg -i $< -ss 127 -t 12 -strict -2 $@<br /> 48 <br /> 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<br /> 50 ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;<br /> 51 ${SH} ffmpeg -y -f concat -i $@.txt -an $@<br /> 52 ${RM} $@.txt<br /></span></span><br /></p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com2tag:blogger.com,1999:blog-4887609088553785394.post-63737534829143261282021-04-16T16:30:00.001-07:002021-04-16T16:30:00.215-07:00Quarantine What's Ups <p> As our pandemic takes it's second revolution around the sun the list of things to occupy the surplus of time has taken a hit. Here are but a number of things I spent time with in 2020 in an attempt to keep busy.</p><p><br /></p><h4 style="text-align: left;">Grow a Beard</h4><p>In the spirit of the 'decade of the unnecessary beard' I stepped forward and extended the traditional facial follicle growing season from the normal Nov-Dec to Nov-Feb, two additional months of woolly cultivation. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkfYecbMFY1iIipfszlZ_GsRUnV49OX_AFOz1pHJNM_A9jq_ovqA-BjcbNAoXY94AnuWQtA38ZMrydGNRQ_LVLlz29ozdRTwvMZB4ZP-ss-w2Bz1GbWQTwsU9QCpPHZVhpkOUVbUmTcnW3/s766/IMG_20210124_100108526.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="574" data-original-width="766" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkfYecbMFY1iIipfszlZ_GsRUnV49OX_AFOz1pHJNM_A9jq_ovqA-BjcbNAoXY94AnuWQtA38ZMrydGNRQ_LVLlz29ozdRTwvMZB4ZP-ss-w2Bz1GbWQTwsU9QCpPHZVhpkOUVbUmTcnW3/s320/IMG_20210124_100108526.jpg" width="320" /></a></div><br /><h4 style="text-align: left;">Culinary Activities</h4><p>Like most home-bound folks, I took a crack at baking my own bread, with modest success. Gone were the Sunday morning breakfast outings, replaced with DIY breakfasts which got better in time. Crepes and french toast made a play into the rotation with hashbrowns and waffles/pancakes taking front seat most pretty frequently. Our love for asian cuisine, particularly Thai, still satisfied by take-out from our favorite restaurant occasionally still left a gap. It's genuinely surprising how a few ingredients can enable homemade Thai, Korean and Chinese dishes. We started making Mango Chicken, Bulgogi and Thai Basil Chicken at home to satisfy the asian cuisine cravings with modest proficiency.</p><h4 style="text-align: left;">On-Line Learning</h4><p>The State of Minnesota offered the DEEDS program, subsidizing on-line learning for the past year. With a surplus of time and interest in a variety of subjects I hammered out 54 courses, ranging from 4-hrs to multiple weeks, in a variety of tech topics. </p><h4 style="text-align: left;">Podcasts</h4><p>One luxury of working from home this past year has been the ability to take an uninterrupted walk over the noon hour, often accompanied with a podcast. With an increase in sitting time, this activity helps my body remember that it's natural state isn't the seated position.</p><h4 style="text-align: left;">Started a Book</h4><p>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. One chapter completed'ish, and it was clear it'd be a substantial effort with likely a limited (or non-existent) audience, so onto the back burner it went.</p><h4 style="text-align: left;">Created a CS Mentoring Website</h4><p>Still amp'd from a desire to help folks into the industry, a frequent contributor to Reddit posts, there seemed a real need for guidance/mentorship for those interested in CS. Personally, I'd have never made it into the profession without some highly dedicated and patient teachers and mentors, so with ample free time I thought the timing would be right for assisting however I could. I was always puzzled when I attended Minnebar (or other conferences) that recommended mentorship but never offered any facilities to connect folks. So I created a mentorship proof-of-concept website, unpublished, to get a feel for what I thought would be beneficial. Then, I reached out to local communities (e.g. Twitter) to gauge interest and I found non-existent interest in the subject. Onto the back-burner it went.</p><h4 style="text-align: left;">Woodworking</h4><p>I've always enjoyed woodworking, but mostly focused on 'big' stuff.....decks, sheds, firepits, stairs....but always wanted to get proficient in high-detailed work. Garage Therapy over the past year allowed me to build my first indoor furniture piece, a variety of small trinkets (spoons, trays), cutting boards, bird feeders as well as a variety of failures destined for firewood. With quality wood prices skyrocketing, and it becoming a more popular hobby over the year it's likely less expensive to take up cocaine as a recreational activity, but in the meantime I hone my skills on big-box-store cheaper lumber with hope of one day using quality woods.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPPqyc9XpyJW6u9qR9EwqmjTnvORMClHOqVeLL84em2Tev8VfHlNCu_2Ac-SaloH3PFTX12zxgM2b4jtdViSXtG6FibG84nv9LxGuZkMl20SLfp2RKPfWfNBVB2QVVzzEYJ7P5YRZ_itY3/s684/IMG_20201129_183708290.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="513" data-original-width="684" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPPqyc9XpyJW6u9qR9EwqmjTnvORMClHOqVeLL84em2Tev8VfHlNCu_2Ac-SaloH3PFTX12zxgM2b4jtdViSXtG6FibG84nv9LxGuZkMl20SLfp2RKPfWfNBVB2QVVzzEYJ7P5YRZ_itY3/s320/IMG_20201129_183708290.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJLZdNa28qNg_UQPPMmAKJf2DAQ6lVPdvTtFwJybIxipy-yAw5w-nVpOi7RWojrrP5MMmFNr_6tCsVJghOxX5q3aHi0IWI6jzS0_vE0aIC9UMyToT9quV7-BfO0cUDDqkardZhgulcy83K/s513/IMG_20201213_153330542.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="513" data-original-width="385" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJLZdNa28qNg_UQPPMmAKJf2DAQ6lVPdvTtFwJybIxipy-yAw5w-nVpOi7RWojrrP5MMmFNr_6tCsVJghOxX5q3aHi0IWI6jzS0_vE0aIC9UMyToT9quV7-BfO0cUDDqkardZhgulcy83K/s320/IMG_20201213_153330542.jpg" /></a></div><br /><p><br /></p><h4 style="text-align: left;">Cleaned House</h4><p>Dozens of boxes of donations have recently bid adieu, hopefully finding their way to a someone who could better use them. Our house, considerably lighter, partly due to thousands of papers being electronically scanned before being recycled. I'm a self-professed hoarder in some regards, especially with respect to tech information. Some of the most inspiring training I had early in my career really set the stage for my success in the profession, I've always felt fortunate for such a investments and retained them for reference. Kinda like old family photos, there is reassurance knowing you have them despite rarely even looking at them. Now, they virtually sit in a backed up repository never to be lost while freeing cubic yards of space in the office(s). Similarly, boxes of old family photos were scanned in the same manner and distributed to the family for similar reasons.</p><h4 style="text-align: left;">Meetups</h4><p>I've always admired proficient tech speakers and genuinely miss the days of attending colloquiums held by passionate and knowledgeable speakers. Like 94.7% of people (a statistic I just made up) who would rather face a rabid grizzly bear than speak in front of an audience I figured what time better than to dive into the pool of fear and uncomfort. Four presentations under my current belt, including ones that I intentionally didn't rehearse to death, an attempt to address my fear of being asked a question I wasn't prepared for. A patient and understanding audience really makes all the difference, each time being easier than the last. </p><h4 style="text-align: left;">In My Humble Opinion</h4><p>I've had dozens of 'passion projects', software projects you do in your spare time in an effort to provide something useful to the world. This project, a distributed survey community, combined cloud computing, Android development, security, and a butt-load of interesting technologies into something I thought would be interesting and useful. To date, this is the last significant project I've worked on, abandoning it after it failed to generate any real interest. Learned a ton of new tech and knocked off the rust on some old skills but back-burnered it as it didn't appear to have a real track in interest.</p><h4 style="text-align: left;">Blogging</h4><p>I've been blogging on-and-off for years now, most recently a solid 2-year weekly contribution here. I feel I gave it a solid try and accomplished what I originally set out to do, but disappointed in not feeling it's really of value beyond myself. For the past couple years, I established a queue of 'waiting to publish' articles and published weekly, releasing all unpublished articles in one fatal swoop a few months ago. Awaiting inspiration to return for another 'go'.</p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-67606875777466143692021-03-16T11:35:00.002-07:002021-03-16T11:35:37.076-07:00Data Visualization with PyGal -- Pymntos<p>Recently presented 'Data Visualization with PyGal' to the Python Mn Meetup group <a href="https://www.meetup.com/PyMNtos-Twin-Cities-Python-User-Group/">https://www.meetup.com/PyMNtos-Twin-Cities-Python-User-Group/</a></p><p>A pretty remarkable group, full of very knowledgeable and supportive individuals. I'd highly recommend attending this meetup if you're interested in Python, ranging from beginner to advanced. </p><p>I slapped together a video on YouTube for your amusement;</p><p></p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/Gco4BbuwHa0" width="320" youtube-src-id="Gco4BbuwHa0"></iframe></div><br /><p></p><p>Slides and code segments are available at our GitHub repo: <a href="https://github.com/fsk-software/pub">https://github.com/fsk-software/pub</a></p><p>Enjoy.</p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-62797087058695393192021-03-13T20:38:00.007-08:002021-03-13T20:38:45.245-08:00RealTime Adjustable FFmpeg Filters -- Part 1<div style="text-align: justify;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkj7NvxZ3zW2m2UcLcePZTPebLwSgCsmlYZfRIbT7HLWeej-mZ1QuBPS34h_u3JIsVC2sdZot2VxmQKqP7aUBn6DsOK5nPdxzpkePKCieER4Rz4S-sxvtH1NBUtJM5fPW_v46j-1j1a2As/s1200/1200px-6DOF.svg.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="1139" data-original-width="1200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkj7NvxZ3zW2m2UcLcePZTPebLwSgCsmlYZfRIbT7HLWeej-mZ1QuBPS34h_u3JIsVC2sdZot2VxmQKqP7aUBn6DsOK5nPdxzpkePKCieER4Rz4S-sxvtH1NBUtJM5fPW_v46j-1j1a2As/s320/1200px-6DOF.svg.png" width="320" /></a></div>
<div style="text-align: center;"><br /></div>
FFmpeg allows dynamic filter adjustments, in past posts we've demonstrated how filters can be adjusted with respect to frame number and/or timestamp. A more unconventional use allows real-time adjustments of filters by an independent process. FFmpeg allows the ability of sending filter parameters by means of the 'zmqsend' utility. This would allow modifying filter parameters during the processing pipeline.<br />
<br />
Two things before we jump into the details; 1) only a select list of filters allow dynamic modifications, and 2) this is an non-standard feature which must be enabled as part of building FFmpeg from source.<br />
<br />
<h3>
Building with ZMQ Support</h3>
The zmqsend utility is used for sending filter parameters into the processing pipeline. This utility utilizes ZMQ libraries, to enable you need to install the zmq libraries followed by enabling zmq support when building FFmpeg;<br />
<br />
<h4>
Install ZMQ Libraries</h4>
<span style="font-family: "courier new", courier, monospace; font-size: x-small;">$ sudo apt-get install -y libzmq3 libzmq3-dev</span><br />
<br />
<h4>
Build FFmpeg</h4>
<div>
Building FFmpeg from source is done via the autotools process: configure, make, make install. Typically I build utilizing the following flags, but the purposes of this post the '--enable-libzmq' serves the trick;</div>
<div>
<br /></div>
<span style="font-family: "courier new", courier, monospace; font-size: x-small;">$ ./configure --enable-libx264 --enable-nonfree --enable-gpl --enable-libfreetype --enable-libmp3lame <b>--enable-libzmq</b></span><br />
<b><br /></b>
<br />
<h3>
Simple Example</h3>
FFmpeg documentation provides a simple example; <a href="https://ffmpeg.org/ffmpeg-filters.html#zmq_002c-azmq" target="_blank">https://ffmpeg.org/ffmpeg-filters.html#zmq_002c-azmq</a><br />
<br />
This simple example does little more than demonstrate sending in parameters. Let's focus on dynamically changing a crop filter instead.<br />
<br />
Suppose you have a video presentation with a moving speaker and you wish to crop to the presenter while in motion. Doing so can't readily be done by a time/frame-based equation and opens a door for using real-time parameter adjustments.<br />
<br />
The following video snippet is that of a presentation I did some months ago. The camera captures the 'stage'.<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzoeKTCZhsb3sCqjlNkKbO-RHUdk0_l2wc9wogbjMhxwjzuiAfRzgfyj-TSwtdC1Q-R7FkYNr4PcN0OLIUg1Q' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Let's say we want to crop a 640x480 region, then pad it to 720x480 we would specify a filter similar to this: <i>-filter_complex "crop=640:480:0:0,pad=720:480:(ow-iw)/2:(oh-ih)/2"</i></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The filter graph of this takes the form:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOIXVoZORRtQlchWDgD1Yyvgmmhr1rI8_TwS1tShVQ873vaRbFJmnewAs9SRPbzJm8Fry0jF31p9YN-f8yvBRghQMrgJBNLZWQl_BSVfeAMtiVvyZ95OsJEjiorFz4hpJL0v1Y_8qO8L2e/s1600/graphXX.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="76" data-original-width="1387" height="35" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOIXVoZORRtQlchWDgD1Yyvgmmhr1rI8_TwS1tShVQ873vaRbFJmnewAs9SRPbzJm8Fry0jF31p9YN-f8yvBRghQMrgJBNLZWQl_BSVfeAMtiVvyZ95OsJEjiorFz4hpJL0v1Y_8qO8L2e/s640/graphXX.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Of particular interest, the 'Parsed_crop_1' filter parameter name is needed for our purposes. The x and y paramets specify the upper-left position of the crop window; refer <a href="https://ffmpeg.org/ffmpeg-filters.html#crop" target="_blank">https://ffmpeg.org/ffmpeg-filters.html#crop</a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
So the idea is to begin the processing of the video file, then send in Parsed_crop_1 x and y parameter values.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "courier new", courier, monospace; font-size: x-small;">$ ffmpeg -y -i input.mp4 -filter_complex "zmq,crop=640:480:0:0,pad=720:480:(ow-iw)/2:(oh-ih)/2" -acodec copy crop.mp4</span></div>
<br />
The following command would update the x location to 100, albeit an jump motion;<br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;">$ echo Parsed_crop_1 x 100 | zmqsend</span><br />
<br />
A slightly more complex motion would be to slowly adjust (x,y) positions in a linear manner;<br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;">$ cat mover.sh </span><br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;">#!/bin/bash</span><br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;">for n in `seq 1 375`; do</span><br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;"> echo $n</span><br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;"> echo Parsed_crop_1 x $n | zmqsend</span><br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;"> echo Parsed_crop_1 y $n | zmqsend</span><br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;"> sleep 0.1</span><br />
<span style="font-family: "courier new", courier, monospace; font-size: x-small;">done;</span><br />
<div>
<br /></div>
<div>
The result would take the form of this:</div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzPjMlnMReuZIQ5KHd-fBE76pxtrRDdSbWaYoqahC58C1nV5FxoOxqbt64j-0VoXSeyTbUjuOlN4gGmK3VtHw' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<div>
<br /></div>
This example again is not very useful, consider it a building block to a more sophisticated example which will be the subject of a future post.<br />
<br />
Cheers.Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-82388987111720957672021-03-13T20:38:00.006-08:002021-03-13T20:38:37.098-08:00Formatting Numerics with Bash<p style="margin: 0px;">Seem like every couple months I need to re-look up how to perform numeric formatting with bash. Here's a quick example of how to do it.</p><p style="margin: 0px;"><br /></p><pre style="margin-bottom: 0px; margin-top: 0px;"><code>$<font face="courier" size="2"> cat /tmp/go
#!/bin/bash
for i in `seq 100`; do
N=$(printf %03d $i)
echo $N
done
</font></code></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><code><font face="courier" size="2">$ ./go
001
002
003
...</font></code></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><code><font face="courier" size="2">097
098
099
100
</font></code></pre><div><code><br /></code></div><div><br /></div><p style="margin: 0px;">I find doing this that one is no longer the loneliest number, it's paired with a couple of real zeros, but never lonely.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Cheers.</p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-78368458212424569192021-03-13T20:38:00.005-08:002021-03-13T20:38:30.959-08:00Linux Find Command with 'or' <div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrzZTUYH4qYgiaPm66qO6hYhw0xXjY2AEDtUufWthZxBAHbeEMoB9VVL-3hic4WSxetTlnnygmu0mUjgOJ_t3j4Uvu7arx1N-BthAcsJlh5G3vM1XMn_E3pjtIDPYkhLlugN19wkwxbgh3/s640/find2.png" style="display: block; padding: 1em 0px;"><img border="0" data-original-height="400" data-original-width="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrzZTUYH4qYgiaPm66qO6hYhw0xXjY2AEDtUufWthZxBAHbeEMoB9VVL-3hic4WSxetTlnnygmu0mUjgOJ_t3j4Uvu7arx1N-BthAcsJlh5G3vM1XMn_E3pjtIDPYkhLlugN19wkwxbgh3/s0/find2.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p style="margin: 0px;"><br /></p><p style="margin: 0px;">On a number of occasions I found it necessary to locate a list of files by extension (e.g. header/source files) and found that if I needed to locate files of multiple extensions I took the 'cowards way out', executing two 'find' commands and appending the results to a temporary output file, then using the output file as input to the next command in the pipeline (e.g. grep).</p><p style="margin: 0px;">e.g.</p><p style="margin: 0px;">$ find . -type f -name "*.c*" > /tmp/junk</p><p style="margin: 0px;">$ find . -type f -name "*.h*" >> /tmp/junk</p><p style="margin: 0px;">$ grep -l SetEvent `cat /tmp/junk`</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Every single time I do this, one of the voices in my head will mock me in the manner of Monty Python, apparently one of the voices in my head is from medieval France with a talent for hurling insults.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">So, today we begin a short journey to the correct way to utilize the 'or' functionality in a Unix find command.</p><p style="margin: 0px;"><br /></p><pre style="margin-bottom: 0px; margin-top: 0px;"><code><br /><font face="courier" size="2">$ find . -type f \( -name "*.c*" -o -name "*.h*" \) -exec grep -l SetEvent {} \;<br /></font></code></pre><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">The above command will locate all header and implementation files (e.g. *.h, *.hpp, *.c, *.cpp...) and return a list of files that contain the SetEvent expression within them. Note the parenthesis are significant to ensure the or'd list is piped to the exec command, otherwise only the second extension list with run through the exec command.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Cheers.</p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-79814785916743835852021-03-13T20:38:00.004-08:002021-03-13T20:38:24.947-08:00Linux Simple Text-to-Speech<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrriiAjVUgSg7xSdCjf390tkRAkxJI4C6K5Qv8yyJFLvlDA0Th2Bha8O_XbGvEzdujrSnkl_zP-rpiil6dLB_A-CrTbkzk169Cj8HOqdBKaeJ6DmcUrV5LxTbdGevEfD_x8RJ11nLzlohv/s200/1425923-200.png" style="display: block; padding: 1em 0px;"><img border="0" data-original-height="200" data-original-width="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrriiAjVUgSg7xSdCjf390tkRAkxJI4C6K5Qv8yyJFLvlDA0Th2Bha8O_XbGvEzdujrSnkl_zP-rpiil6dLB_A-CrTbkzk169Cj8HOqdBKaeJ6DmcUrV5LxTbdGevEfD_x8RJ11nLzlohv/s0/1425923-200.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p style="margin: 0px;"><br /></p><p style="margin: 0px;">As we enter our 6th month of self-quarantining, I find the hermit lifestyle no longer to my liking. A virtual friend is needed and this post, my friend, is your ticket to electronic companionship. Enjoy.</p><p style="margin: 0px;"><br /></p><pre style="margin-bottom: 0px; margin-top: 0px;"><code><br /># apt-get install espeak<br /></code></pre><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Then, simply run it.</p><p style="margin: 0px;"><br /></p><pre style="margin-bottom: 0px; margin-top: 0px;"><code><br />$ espeak "hello, how are you doing?"<br /></code></pre><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Whelp, look at you! You made it to the end you big big overachiever. <i><b>If</b></i> you found enjoyment in this particular post, please consider seeking counsel, there are medications and therapy practices that can snap you right quick outta that.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Cheers.</p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-37189938783399828182021-03-13T20:38:00.003-08:002021-03-13T20:38:19.150-08:00Tcpdump Examples<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHf5JpExjwpuiNu9ahb1SgM03XjYspRUjMw_t6f-UTC_Y7aDV3ujRnbwKC3acEWiHX4OD2Tsnsma_jcw36oxiQoiX0L_jQYI-XXp35O6hqeICYOY9gI7Ji9j9Ticw-EqnrIGadES31Zoog/s1193/Tcpdump_4.9.3_screenshot.png" style="display: block; padding: 1em 0px;"><img border="0" data-original-height="727" data-original-width="1193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHf5JpExjwpuiNu9ahb1SgM03XjYspRUjMw_t6f-UTC_Y7aDV3ujRnbwKC3acEWiHX4OD2Tsnsma_jcw36oxiQoiX0L_jQYI-XXp35O6hqeICYOY9gI7Ji9j9Ticw-EqnrIGadES31Zoog/s640/Tcpdump_4.9.3_screenshot.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p style="margin: 0px;"><br /></p><p style="margin: 0px;">As much as I like using Wireshark, I'm a sucker for a command interface. The trouble is properly defining a filter has been difficult to say the least, boring simple post, but below is a list of brief examples and descriptions.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#!/bin/bash</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 -w /tmp/data.pcap</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets to/from host and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 host 192.168.1.125 -w /tmp/data.pcap</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets from host and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 src host 192.168.1.125 -w /tmp/data.pcap</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets to host and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 dst host 192.168.1.125 -w /tmp/data.pcap</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets to/from port and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 port 80 -w /tmp/data.pcap</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets to/from net and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 net 192.168.1 -w /tmp/data.pcap</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets from network and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 src net 192.168.1 -w /tmp/data.pcap</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets from network and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 dst net 192.168.1 -w /tmp/data.pcap</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">#--grab the first 10 packets from network and write to file</p><p style="margin: 0px;"> tcpdump -c 10 -i wlan0 '(dst host 192.168.1.125) and (port 443)' -w /tmp/data.pcap</p><div><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Now, go put your virtual ear to a NIC and hear whatcha hear.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Cheers.</p></div>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-11697714624170338412021-03-13T20:38:00.002-08:002021-03-13T20:38:12.595-08:00C Storage Classes<div><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq9F5MZ2J3S35ODvu_6ZWWiDty6VBBZqaGLoBueFdL1uGpVg-7ps0B1BQdL32BMakt_rK7QZFtI96d_F4sVKbH58qidw9FqEsKazkS6w_VSn_X7rVaANEaKEHJG4IlwfmcKfBECxBi8j5h/s1500/71VI3DDpx5L._AC_SL1500_.jpg" style="display: block; padding: 1em 0px;"><img border="0" data-original-height="1142" data-original-width="1500" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq9F5MZ2J3S35ODvu_6ZWWiDty6VBBZqaGLoBueFdL1uGpVg-7ps0B1BQdL32BMakt_rK7QZFtI96d_F4sVKbH58qidw9FqEsKazkS6w_VSn_X7rVaANEaKEHJG4IlwfmcKfBECxBi8j5h/s640/71VI3DDpx5L._AC_SL1500_.jpg" width="640" /></a></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><span face="georgia, "times new roman", sans-serif" style="background-color: #fff3db; color: #29303b; font-size: 13px;"><br /></span></div>In investigating the proper means to declare a global variable, I found myself reviewing some C documentation that I had long forgotten, concerning the Storage Class.<br /><br />To briefly review, each variable declaration consists of 3 elements: the storage class, the type, and the variable name. For example:<br style="background-color: #fff3db; color: #29303b; font-family: georgia, "times new roman", sans-serif; font-size: 13px;" /><br style="background-color: #fff3db; color: #29303b; font-family: georgia, "times new roman", sans-serif; font-size: 13px;" /><code style="background: rgb(0, 0, 0) none repeat scroll 0% 0%; color: white; display: block; font-style: italic; overflow: auto; width: 425.688px;"><br />auto int x;<br />static int y;<br /></code><br style="background-color: #fff3db; color: #29303b; font-family: georgia, "times new roman", sans-serif; font-size: 13px;" /><br />The storage class can take the form of auto, extern, static, or register. If not specified, the implicit storage class is that of auto.<br /><br />The auto storage class is the most frequently used of the classes, primarily because it is the implicit default. Local variables take this form, where the storage is not allocated until the block in which the variable is defined is entered.<br /><br />The extern storage class specified simply a reference to a variable that is defined elsewhere. Space is therefore not allocated upon encountering this reference, since the storage is allocated elsewhere. This as you may recall is the means to declare a global variable.<br /><br />The static storage class specifies that the variable cannot be access by functions outside the translation unit in which it was defined. A common error is declaration of a static variable in a header file, which is imported by more than one translation unit not understand that each translation unit essentially created independent copies of the variable. A common practice of declaring a static constant in a header, used by multiple translation units results in multiple copies of the constant, in each translation unit. However, if the static variable simply defines a constant....generally, no-harm-no-foul. It is however worth understanding that it is not a shared reference.<br /><br />The last storage class is that of register, which notifies the compiler to make the variable as efficient as possible. Ideally, the variable will retain it's location in a cpu register for optimal performance.Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-53418207755459542152021-03-13T20:38:00.001-08:002021-03-13T20:38:06.177-08:004 Reference Books Every C++ Developer Should Have<div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhMdaS1oNwp06WxXu_VRG375vrhSLKYiQMp4PU-4K6JePNh2xi_CAfAVks8Ej4SOe4mynabpXsdbS7pglr1zqm9sC8mTcfQiruE7eKLEqpfoM4_v8y7X83d9h60wy8n5dl3KL8ebYAtvgu/s384/books-sm.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="384" data-original-width="326" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhMdaS1oNwp06WxXu_VRG375vrhSLKYiQMp4PU-4K6JePNh2xi_CAfAVks8Ej4SOe4mynabpXsdbS7pglr1zqm9sC8mTcfQiruE7eKLEqpfoM4_v8y7X83d9h60wy8n5dl3KL8ebYAtvgu/s320/books-sm.png" /></a></div><span face="georgia, "times new roman", sans-serif" style="background-color: #fff3db; color: #29303b; font-size: 13px;"><br /></span></div>Over the years development languages change from project-to-project. A challenge for learning a language involves selection of what references are worthwhile. Far too often I've made poor selections in textbooks and selecting C++ references some years back was no exception.<br /><br />I'd like to list the top 4 books that I've selected for my arsenal that I feel are essential to doing proper C++ (in no particular order):<br /><h4 style="text-align: left;"><font face="arial">The C++ Programming Language - Stroustrup</font></h4>To understand C++, or any other language, you've gotta understand the language syntax and semantics. The rules...what's allowed and what is not. Who better to inform you of such things than the father of the programming language; nuff said.<div><h4 style="text-align: left;"><font face="arial">C++ FAQs 2nd Edition - Marshall Cline</font></h4>After you know the language syntax and semantics, the next thing you should be educated in is what should you do and what shouldn't you do. Marshall Cline identifies many of the pitfalls of common practices in an educational and surprisingly entertaining manner. I've attended a series of training sessions with the man himself and I highly recommend both the book and the courses if you can get someone else to pay for it :)<br /><h4 style="text-align: left;"><font face="arial">Effective C++ 3rd Edition - Scott Meyer</font></h4>I'd consider this book to be the Bible with respect to best C++ programming practices. I cannot say enough good things about this book, but be forwarned...it expects you to understand the language to a fair degree. Don't buy this as your introduction to C++, buy this as a moderately experienced developer.<br /><h4 style="text-align: left;"><font face="arial">Effective STL - Scott Meyer</font></h4>What is C++ without the STL.....the equivalent of a three-legged dog; never living up to the potential of a 4-legged dog. Come on, most interesting problem domains are complex enough to benefit from utilizing the STL including a great deal of embedded software products.<br /><br />Scott Meyer for the win, leading 2 books to 1 in the competition; actually, the 'Effective ...' books are closer to the style of cliff notes, paling in comparison of sheer paper girth. <div><br /></div><div>Likely no surprise to anyone, the creator of the language (Stroustrup) lays the foundation of the language, how to do nearly anything. Marshall Cline lays our best practices, things that you can do with the language but probably shouldn't. Scott Meyer's books take the same tact, only in a highly compressed format.</div><div><br />These are the books that I'd recommend to anyone developing in C++. I have a set of Sutter and Koenig books available to me and I've heard great things and have referenced them on occasion but not yet to give them sufficient time to reading them.</div><div><br /></div><div>Enjoy your journey into an amazing language.</div><div><br /></div></div>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-28617148384229800992021-03-13T20:38:00.000-08:002021-03-13T20:38:00.318-08:00Playing Raw Video with Mplayer <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBNCJs2hhBKTyPCI7osx_PiU7gq6GPUZxe_g8_-CgeoBI30NueTgOqsUGKn6nF5NNyen4Ps0aSk6g61O8OJHi0WrL5QTh8zVlQbeZmiCPYTg88NWJFIXAHZZMiXqK4FS5_vpDDIWAIbMV1/s1200/1200px-Wikipedia_favicon_hexdump.svg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1034" data-original-width="1200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBNCJs2hhBKTyPCI7osx_PiU7gq6GPUZxe_g8_-CgeoBI30NueTgOqsUGKn6nF5NNyen4Ps0aSk6g61O8OJHi0WrL5QTh8zVlQbeZmiCPYTg88NWJFIXAHZZMiXqK4FS5_vpDDIWAIbMV1/s320/1200px-Wikipedia_favicon_hexdump.svg.png" width="320" /></a></div><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Mplayer adopts the 'can do' attitude as a media player. Generally, this utility will play most anything, employs most standard codecs and many that you'll likely never need. If you have a video file, likely you can play it with Mplayer...including raw data.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">A past program I was working on grabbed raw LVDS data streams from a Flir infrared camera. First task, confirm the video output is reasonable. Cue investigation of how to play raw data streams without requiring encoding. Mplayer met this need quite nicely. For fun, I thought I'd post some of our findings, hopefully you can find them useful.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Lets start with generating our own raw video stream:</p><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="font-family: courier; font-size: small;"><font>#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <stdio.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <stdlib.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font>.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font>><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <fcntl.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <assert.h><br /><br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> main()<br />{<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> NumFrames = <font color="#800000" data-blogger-escaped-style="color:Maroon;">10</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> Width = <font color="#800000" data-blogger-escaped-style="color:Maroon;">640</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> Height = <font color="#800000" data-blogger-escaped-style="color:Maroon;">480</font>;<br /><br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) main process initializing\n"</font>,__FILE__,__LINE__);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">std</font>::<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font> fileName=<font color="#800000" data-blogger-escaped-style="color:Maroon;">"/tmp/video.raw"</font>;<br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) fileName='%s'\n"</font>,__FILE__,__LINE__,fileName.c_str());<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);<br /><br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font> (<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> i=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; i<NumFrames; ++i)<br />{<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font> frame[Width*Height];<br /><br /><font color="#008000" data-blogger-escaped-style="color:Green;">// generate frame; white box, framed with black lines</font><br />memset (frame,<font color="#800000" data-blogger-escaped-style="color:Maroon;">255</font>,Width*Height);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> c=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; c<Width; ++c) frame[c+Width*<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> c=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; c<Width; ++c) frame[c+Width*(Height-<font color="#800000" data-blogger-escaped-style="color:Maroon;">1</font>)]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> h=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; h<Height; ++h) frame[<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>+Width*h]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> h=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; h<Height; ++h) frame[<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>+Width*h+(Width-<font color="#800000" data-blogger-escaped-style="color:Maroon;">1</font>)]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><br /><font color="#008000" data-blogger-escaped-style="color:Green;">// write frame contents to file</font><br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> k=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; k<Width*Height; ++k)<br />{<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">void</font>* tmp=(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">void</font>*)(frame+k);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> ret=write(fp,tmp,<font color="#0000ff" data-blogger-escaped-style="color:Blue;">sizeof</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font>));<br />assert(ret==<font color="#0000ff" data-blogger-escaped-style="color:Blue;">sizeof</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font>));<br />}<br />}<br /><br />close(fp);<br /><br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) main process terminating\n"</font>,__FILE__,__LINE__);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">return</font> EXIT_SUCCESS;<br />}</font><br /></span></pre><p style="margin: 0px;"><span style="font-family: courier; font-size: small;"><br /></span></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">The above code simply generates a sequence of 10 640x480 8-bit grayscale frames, the content of each frame a white background framed with a black one-pixel wide border.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">You can play the video contents of this file by issuing the following Mplayer command:</p><pre style="margin-bottom: 0px; margin-top: 0px;"><code><br /><font face="courier" size="2">$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307200:fps=1 /tmp/video.raw<br /></font></code></pre><p style="margin: 0px;"><br /></p><p style="margin: 0px;">A few things worth mentioning; first, since this is a raw video the frame size must be specified (ie. w=640:h=480), the frame rate (fps=1) must be specified as well or it will default to 24fps, giving us little time to review the contents of the video. Lastly, the size=307200 (640*480) is optional at for this format, but will become relevant in later examples. By default, the size will be defined by the width*height, so for this example you could simply not specify the value and all would still be well.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">That was fun, but let's give it a little more snap. Suppose you wanted to add some frame metadata following each frame. We'll modify our above example a bit, each frame will be followed by a fixed 28-byte character string identifying the frame number.</p><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="font-family: courier; font-size: small;"><font>#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <stdio.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <stdlib.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font>.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font>><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <fcntl.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <assert.h><br /><br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> main()<br />{<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> NumFrames = <font color="#800000" data-blogger-escaped-style="color:Maroon;">10</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> Width = <font color="#800000" data-blogger-escaped-style="color:Maroon;">640</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> Height = <font color="#800000" data-blogger-escaped-style="color:Maroon;">480</font>;<br /><br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) main process initializing\n"</font>,__FILE__,__LINE__);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">std</font>::<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font> fileName=<font color="#800000" data-blogger-escaped-style="color:Maroon;">"/tmp/video.raw"</font>;<br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) fileName='%s'\n"</font>,__FILE__,__LINE__,fileName.c_str());<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);<br /><br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font> (<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> i=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; i<NumFrames; ++i)<br />{<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font> frame[Width*Height];<br /><br /><font color="#008000" data-blogger-escaped-style="color:Green;">// generate frame; white box, framed with black lines</font><br />memset (frame,<font color="#800000" data-blogger-escaped-style="color:Maroon;">255</font>,Width*Height);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> c=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; c<Width; ++c) frame[c+Width*<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> c=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; c<Width; ++c) frame[c+Width*(Height-<font color="#800000" data-blogger-escaped-style="color:Maroon;">1</font>)]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> h=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; h<Height; ++h) frame[<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>+Width*h]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> h=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; h<Height; ++h) frame[<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>+Width*h+(Width-<font color="#800000" data-blogger-escaped-style="color:Maroon;">1</font>)]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><br /><font color="#008000" data-blogger-escaped-style="color:Green;">// write frame contents to file</font><br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> k=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; k<Width*Height; ++k)<br />{<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">void</font>* tmp=(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">void</font>*)(frame+k);<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> ret=write(fp,tmp,<font color="#0000ff" data-blogger-escaped-style="color:Blue;">sizeof</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font>));<br /> assert(ret==<font color="#0000ff" data-blogger-escaped-style="color:Blue;">sizeof</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font>));<br />}<br /><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> </font><font color="#008000" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Green;" style="font-style: italic; font-weight: bold;">//write some metadata</font><br /><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> </font><font color="#0000ff" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Blue;" style="font-style: italic; font-weight: bold;">char</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> metaData[</font><font color="#800000" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Maroon;" style="font-style: italic; font-weight: bold;">28</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;">];</font><br /><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> sprintf(metaData,</font><font color="#800000" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Maroon;" style="font-style: italic; font-weight: bold;">"frame%04d"</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;">,i);</font><br /><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> assert(write(fp,metaData,</font><font color="#0000ff" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Blue;" style="font-style: italic; font-weight: bold;">sizeof</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;">(metaData))==</font><font color="#0000ff" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Blue;" style="font-style: italic; font-weight: bold;">sizeof</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;">(metaData));</font><br />}<br /><br />close(fp);<br /><br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) main process terminating\n"</font>,__FILE__,__LINE__);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">return</font> EXIT_SUCCESS;<br />}</font><br /></span></pre><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Now the frame size becomes a required argument, since the frame size must now account for the metadata, otherwise the metadata would be interpreted as video and you'd have a mess. You can play the above generated video by issuing the command as follows:</p><pre style="margin-bottom: 0px; margin-top: 0px;"><code><br /><font face="courier" size="2">$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307228:fps=1 /tmp/video.raw<br /></font></code></pre><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Ok, now we've learned how to play video with metadata. Our last trick will be playing video with a initial video header.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Let's start by adding some video metadata at the beginning of the video, a 200-byte string.</p><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="font-family: courier; font-size: small;"><font>#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <stdio.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <stdlib.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font>.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font>><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <fcntl.h><br />#<font color="#0000ff" data-blogger-escaped-style="color:Blue;">include</font> <assert.h><br /><br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> main()<br />{<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> NumFrames = <font color="#800000" data-blogger-escaped-style="color:Maroon;">10</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> Width = <font color="#800000" data-blogger-escaped-style="color:Maroon;">640</font>;<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">static</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">const</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> Height = <font color="#800000" data-blogger-escaped-style="color:Maroon;">480</font>;<br /><br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) main process initializing\n"</font>,__FILE__,__LINE__);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">std</font>::<font color="#0000ff" data-blogger-escaped-style="color:Blue;">string</font> fileName=<font color="#800000" data-blogger-escaped-style="color:Maroon;">"/tmp/video.raw"</font>;<br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) fileName='%s'\n"</font>,__FILE__,__LINE__,fileName.c_str());<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);<br /><br /><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> </font><font color="#0000ff" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Blue;" style="font-style: italic; font-weight: bold;">char</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> videoMetaData[</font><font color="#800000" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Maroon;" style="font-style: italic; font-weight: bold;">200</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;">];</font><br /><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> sprintf(videoMetaData,</font><font color="#800000" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Maroon;" style="font-style: italic; font-weight: bold;">"some cool video"</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;">);</font><br /><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;"> assert(write(fp,videoMetaData,</font><font color="#0000ff" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Blue;" style="font-style: italic; font-weight: bold;">sizeof</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;">(videoMetaData))==</font><font color="#0000ff" data-blogger-escaped-style="font-weight: bold; font-style: italic;color:Blue;" style="font-style: italic; font-weight: bold;">sizeof</font><font data-blogger-escaped-style="font-weight: bold; font-style: italic;" style="font-style: italic; font-weight: bold;">(videoMetaData));</font><br /><br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font> (<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> i=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; i<NumFrames; ++i)<br />{<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font> frame[Width*Height];<br /><br /> <font color="#008000" data-blogger-escaped-style="color:Green;">// generate frame; white box, framed with black lines</font><br /> memset (frame,<font color="#800000" data-blogger-escaped-style="color:Maroon;">255</font>,Width*Height);<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> c=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; c<Width; ++c) frame[c+Width*<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> c=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; c<Width; ++c) frame[c+Width*(Height-<font color="#800000" data-blogger-escaped-style="color:Maroon;">1</font>)]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> h=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; h<Height; ++h) frame[<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>+Width*h]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> h=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; h<Height; ++h) frame[<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>+Width*h+(Width-<font color="#800000" data-blogger-escaped-style="color:Maroon;">1</font>)]=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>;<br /><br /> <font color="#008000" data-blogger-escaped-style="color:Green;">// write frame contents to file</font><br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">for</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> k=<font color="#800000" data-blogger-escaped-style="color:Maroon;">0</font>; k<Width*Height; ++k)<br /> {<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">void</font>* tmp=(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">void</font>*)(frame+k);<br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">int</font> ret=write(fp,tmp,<font color="#0000ff" data-blogger-escaped-style="color:Blue;">sizeof</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font>));<br /> assert(ret==<font color="#0000ff" data-blogger-escaped-style="color:Blue;">sizeof</font>(<font color="#0000ff" data-blogger-escaped-style="color:Blue;">unsigned</font> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font>));<br /> }<br /> <font color="#008000" data-blogger-escaped-style="color:Green;">//write some metadata</font><br /> <font color="#0000ff" data-blogger-escaped-style="color:Blue;">char</font> metaData[<font color="#800000" data-blogger-escaped-style="color:Maroon;">28</font>];<br /> sprintf(metaData,<font color="#800000" data-blogger-escaped-style="color:Maroon;">"frame%04d"</font>,i);<br /> assert(write(fp,metaData,<font color="#0000ff" data-blogger-escaped-style="color:Blue;">sizeof</font>(metaData))==<font color="#0000ff" data-blogger-escaped-style="color:Blue;">sizeof</font>(metaData));<br />}<br /><br />close(fp);<br /><br />printf(<font color="#800000" data-blogger-escaped-style="color:Maroon;">"(%s:%d) main process terminating\n"</font>,__FILE__,__LINE__);<br /><font color="#0000ff" data-blogger-escaped-style="color:Blue;">return</font> EXIT_SUCCESS;<br />}</font><br /></span></pre><p style="margin: 0px;"><span style="font-family: courier; font-size: small;"><br /></span></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">You play this form of video by specifying a start byte offset of 200 and you're set.</p><pre style="margin-bottom: 0px; margin-top: 0px;"><code><br /><font face="courier" size="2">$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307228:fps=1 -sb 200 /tmp/video.raw</font></code></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><code><font face="courier" size="2"><br /></font></code></pre>Beethoven or a core file, mplayer can play it.<br /><br />Cheers.<br />Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-52868747840792434132021-03-13T20:37:00.008-08:002021-03-13T20:37:54.259-08:00Android Rtsp Usage<p style="margin: 0px;"><a data-blogger-escaped-style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbo7aHNKPiyb1wslpJDtz21ZuS23nl14J2hdt-WsHRtoJSHdU7VWonLPbM7Cf034tyq3cwTbYL4KnTIY6QTmcEqg51703oX30DScSpVrFuZfyY1QMY5XlO4c18_8JdR6dpkaZBkencv1w/s1600/foo.jpg" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><br class="Apple-interchange-newline" /><br /></a>Loved my old phone but recently replaced it. Result is that I have a relatively decent smart phone with little, to no, purpose in life. Repurposing as a RTSP server device seemed like a reasonable task.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Reviewed a series of video server apps in the Google Play Store, but only one that worked well for me with Linux RTSP clients was:</p><p style="margin: 0px;"><a href="https://play.google.com/store/apps/details?id=com.pas.webcam">https://play.google.com/store/apps/details?id=com.pas.webcam</a></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">After installing the app and short configuration, you start the server by clicking the 'Start server' button in the video preference activity.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Once the phone streaming is running, it displays a live view of the scene and the connection URL.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Open it with VLC and you're cookin' with bacon:</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><code></code><br /></p><pre style="margin-bottom: 0px; margin-top: 0px;"><code><span style="font-family: courier; font-size: small;">$ cvlc http://192.168.0.157:8080/video
</span>
</code></pre><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><br /></p><p class="separator" data-blogger-escaped-style="clear: both; text-align: center;" style="clear: both; margin: 0px; text-align: center;"><a data-blogger-escaped-style="margin-left: 1em; margin-right: 1em;" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaMSI4NSwrdt8CMWgEZSQrk9Mj882Au27RnU_2HLIOiw809sof-6pLOOjVsgLYwZ5PRzahJzh5Q-z4IyfMJfL67oo4VYgVavgBQwlHeugJmL-QCBkKfNnkUbrBfgsgFY-xqu4eROxHa2E/s1600/foo.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaMSI4NSwrdt8CMWgEZSQrk9Mj882Au27RnU_2HLIOiw809sof-6pLOOjVsgLYwZ5PRzahJzh5Q-z4IyfMJfL67oo4VYgVavgBQwlHeugJmL-QCBkKfNnkUbrBfgsgFY-xqu4eROxHa2E/s640/foo.jpg" style="cursor: move;" width="640" /></a></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Voilà.</p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-31524894079410771472021-03-13T20:37:00.007-08:002021-03-13T20:37:46.214-08:00Cpu Load Monitoring<div style="background-color: #fff3db; color: #1b0431; font-weight: normal; margin: 0px; padding: 0px;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4J8w0pmMrwvy1vBqMuA8Qy3-i63v4_1_7JXj_G6OQsI29-Uz2aetdvBhBmn56e6mhNu1r3g_ohr_0P3co7vAND5cIm_Bm-SXlJGSWGR6gDn9qnkfdAgnkXN9PTgSyY-9Wr3Lbm6mEzBrc/s491/CPU-load-average-2.png" style="display: block; padding: 1em 0px;"><img border="0" data-original-height="245" data-original-width="491" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4J8w0pmMrwvy1vBqMuA8Qy3-i63v4_1_7JXj_G6OQsI29-Uz2aetdvBhBmn56e6mhNu1r3g_ohr_0P3co7vAND5cIm_Bm-SXlJGSWGR6gDn9qnkfdAgnkXN9PTgSyY-9Wr3Lbm6mEzBrc/s0/CPU-load-average-2.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><span face="georgia, "times new roman", sans-serif"><br /></span></div><div style="background-color: #fff3db; color: #1b0431; font-weight: normal; margin: 0px; padding: 0px;">
<span face="georgia, "times new roman", sans-serif">We've recently had explore collecting and analyzing CPU resource loading metrics for our project. The host OS is Linux 2.6 based and rather than install a performance-specific set of utilities we aimed at utilizing readily available services.</span><br />
<span face="georgia, "times new roman", sans-serif">Ask any Linux weenie what CPU loading they have been experiencing and most will respond by starting the </span><span face="georgia, "times new roman", sans-serif" style="font-style: italic;">top</span><span face="georgia, "times new roman", sans-serif"> utility. </span><span face="georgia, "times new roman", sans-serif" style="font-style: italic;">Top</span><span face="georgia, "times new roman", sans-serif"> is available on most Unix distributions, is a well known utility and has pretty much stood the test of time. For those reasons it seemed obvious to incorporate it into a performance monitoring data collection and analysis tool suite.</span><br />
<span face="georgia, "times new roman", sans-serif">The following Tcl script takes two arguments, the first an output file which will be populated with the redirected </span><span face="georgia, "times new roman", sans-serif" style="font-style: italic;">top</span><span face="georgia, "times new roman", sans-serif"> log file, the second is a duration (in seconds) of how long to monitor the performance. After the raw collection has completed the script parses the log, extracts the CPU loading metrics and outputs a median and mean CPU processing load expressed in percentage of utilization.</span><br />
<span face="georgia, "times new roman", sans-serif">It's worth noting that the script incorporates searching for the regular expression *Cpu* to find the top line item holding the Cpu metrics. As a result, if you have a process running that matches *Cpu* you'll find the script errors out during processing of the log file. I'll leave it as a lesson to the reader how to correct (wink).</span><br />
<span face="georgia, "times new roman", sans-serif">We've extended on the same principle to monitor network activities.</span><br />
<span face="georgia, "times new roman", sans-serif">Have fun.</span><br />
<code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; font-family: georgia, "times new roman", sans-serif; overflow: auto; width: 421.422px;"><br /></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">#!/usr/bin/tclsh</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"><br /></span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">proc log { msg } {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"># puts stderr __$msg</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">}</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"><br /></span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">proc mean { L } {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set sum 0.0</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> foreach e $L {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set sum [expr $sum + $e]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> }</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> return [expr $sum / [llength $L].]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">}</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"><br /></span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">proc median { L } {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set L1 [lsort -real $L]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> return [lindex $L1 [expr [llength $L1] / 2]]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">}</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"><br /></span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">proc process { fileName } {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set fp [open $fileName r]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> while { [gets $fp line] >= 0 } {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> switch -glob -- $line {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> *Cpu*</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "processing line '$line'"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set el [split $line ,]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set userLoad [string trimleft [lindex $el 0] "Cpu(s):"]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set sysLoad [lindex $el 1]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set niceLoad [lindex $el 2]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set idleLoad [lindex $el 3]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "parsing $userLoad"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "parsing $sysLoad"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "parsing $niceLoad"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "parsing $idleLoad"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set userLoad [lindex [split $userLoad \%] 0]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set sysLoad [lindex [split $sysLoad \%] 0]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set niceLoad [lindex [split $niceLoad \%] 0]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> set idleLoad [lindex [split $idleLoad \%] 0]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "extracted $userLoad"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "extracted $sysLoad"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "extracted $niceLoad"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> log "extracted $idleLoad"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"># puts [expr $userLoad+$sysLoad+$niceLoad+$idleLoad]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> lappend L [expr 100.0-$idleLoad]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> }</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> }</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> }</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> close $fp</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> puts "mean cpu load: [mean $L]"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> puts "median cpu load: [median $L]"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">}</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"><br /></span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">proc monitorCpuLoad { fileName duration } {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> catch { exec top -b -n $duration -d 1 > $fileName }</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> process $fileName</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">}</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"><br /></span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">proc help { } {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> upvar #0 argv0 argv0</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> puts stderr "usage: $argv0 \[logFileName\] \[duration\]"</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> exit</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">}</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"><br /></span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;">#---main---</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> if { [llength $argv] != 2 } {</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> help</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> }</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;"><span style="font-family: "courier new", courier, monospace; font-size: x-small;"> monitorCpuLoad [lindex $argv 0] [lindex $argv 1]</span></code><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; font-family: georgia, "times new roman", sans-serif; overflow: auto; width: 421.422px;"><br /></code></div>
<div class="post-header" style="background-color: #fff3db; color: #29303b; font-family: georgia, "times new roman", sans-serif;">
<div class="post-header-line-1">
</div>
</div>
<div class="post-body entry-content" id="post-body-2662117669460538221" itemprop="" style="background-color: #fff3db; color: #29303b; font-family: georgia, "times new roman", sans-serif;">
<pre style="background: rgb(0, 0, 0) none repeat scroll 0% 0%; color: white; font-style: italic; overflow: auto; width: 425.688px;"><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; display: block; overflow: auto; width: 421.422px;">
</code></pre>
</div>
Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-87078879023053834162021-03-13T20:37:00.006-08:002021-03-13T20:37:39.661-08:00Recruiting and Staffing Agencies<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT3zqPg5pUfa4Cms_cyIsZXT1B97yVhKQmxRDAhEgCbcMVVoIukAmQLz5qw8eBVYeS9bg1TM-FsSFbovuPMbsHlFLMGkdRXYC2j9v_VPglJLcHirNetj4-KaHf3ppTyDpI4OtIXv4dCPTX/s1600/calvin_wormwood-21st-century-working.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="275" data-original-width="768" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT3zqPg5pUfa4Cms_cyIsZXT1B97yVhKQmxRDAhEgCbcMVVoIukAmQLz5qw8eBVYeS9bg1TM-FsSFbovuPMbsHlFLMGkdRXYC2j9v_VPglJLcHirNetj4-KaHf3ppTyDpI4OtIXv4dCPTX/s640/calvin_wormwood-21st-century-working.jpg" width="640" /></a></div>
<br />
Employment recruitment and staffing agencies are a pretty standard participant in the software engineering ecosystem. Someone has a job looking to fill, someone wants a job in search of one, these agencies serve as a broker of putting the <i>wanter</i> in the same room with the <i>wantee</i>. <br />
<br />
Over the past years, I've found myself both as a hiring manager as well as a job seeker, so I feel I have some experience in both camps and I thought my observations might be of interest to some.<br />
<br />
<h4>
The Journey of a Resume</h4>
Let's take a journey, shall we? The journey of a your engineering resume begins when one day you receive an e-mail, or perhaps a phone call, from a staffing agency. Perhaps someone you've worked with in the past, perhaps someone you've never heard of. I'll attempt to use the term staffing agency throughout this post, but you may prefer the use of: contracting house, consulting firm, employment services or technical recruiting house. Regardless, they each serve as an agent that introduces those who have jobs with those seeking jobs. These agencies have two primary purposes; tracking available job positions and tracking candidates for said jobs. Staffing agencies act as a middle-man in introducing job seekers with job providers, their fundamental purpose.<br />
<div>
<br /></div>
<div>
The e-mail, or phone conversation, will be detailed in some respects and curiously vague in others. The exchange may call out a good deal of technical details; tooling, software, programming languages and dev-op specifics, but curiously vague in the company and/or exact industry the job exists. This is for good reason; specifically it's an effort to ensure you apply for the position through them rather than directly with the company providing the position, or an alternative agency. If you show interest in the position, or a vague interest in alternative positions, the recruiter will tend to suggest "why don't you fire me an updated resume?". This exchange has some significance which we'll touch on later, but let's continue following the path of your resume for a job you're interested in.</div>
<div>
<br /></div>
<div>
You send your resume to the recruiter for the position, the recruiter slightly tailors your resume to be later sent to the hiring manager (or HR) for consideration. The tailoring is subtle, mostly not of consequence, and has the same desired effect of preventing direct contact between applicant and hiring manager. Your resume, electronic or physical, is stripped of direct contact info; your e-mail address is removed as is any phone number. Typically, your name is preserved, so with modest effort a hiring manager could contact you directly; they don't tend to however because there is a legal, or professional, agreement between the hiring company and agencies that discourages direct exchanges. </div>
<div>
<br /></div>
<div>
At this stage, the recruiter has your resume and it goes to the hiring manager, right? Whelp, not quite, but almost. Before an agency submits your resume they establish legal protections to prevent them from being removed from the hiring process and getting paid. It should come at no surprise that agencies are compensated for their efforts. To prevent a hiring manager directly offering a job to a candidate the agent provided, the hiring company and agency sign legal agreements. These agreements tend to specify that candidates provided by the agency are represented by the agency and cannot be directly approached. Agreements also call out constraints to avoid being kicked to the curb at a later time. For example, most agreements state that if you hire candidate X from agency Y the candidate cannot move to agency Z for the same position. This is to avoid agency Y who may be charging $120/hr to be replaced with agency Z for $110/hr. The candidate is locked to the agency for the existing position. Agreements also call out contract-to-hire positions, where a candidate comes in as a contractor for an hourly rate and is allowed to be hired as a W2 (direct) employee after some period of time if the company wishes to do so. Agreements that specify the contract-to-hire options tend to charge a bit higher rate and likely a transition bonus if the candidate goes direct. A properly placed candidate with long-term potential is like an egg-laying goose, locking in a candidate to a long-term hourly position is extremely profitable so contract-to-hire positions tend to adjust the rate higher to offset the loss or make up for it with an agency hiring bonus. Agency compensation details (like invoiced hourly rates or hiring bonuses) are considered competition-sensitive and typically prevented in the form of a non-disclosure agreement (NDA). Signed agreements in place, your resume is forwarded to the hiring manager. </div>
<div>
<br /></div>
<div>
If the hiring manager feels you're a good fit they will contact the agent and you will proceed on, likely with a phone interview or on-site interview. Your resume is then likely kept on file at the staffing agency for future opportunities.</div>
<div>
<br /></div>
<h4>
Job Position Evaluation Process </h4>
<div>
Once the resume hits the hiring manager's hands, the staffing agency continues as a middle-man between the hiring manager and applicant. At each stage of the hiring process, the agent participates in the process. Often, many communications between hiring manager and candidate flows through the agent. This can prove to be useful in uncomfortable conversations, like negotiations, so it can be beneficial to the applicant as well as the hiring manager. If you're a good fit you'll likely proceed on with the hiring process, if not the agent will typically discuss specific mismatches to be used to tailor their search for alternative candidates. Exchanges between a hiring manager and candidate are restricted as a result of pre-existing agreements prevented by existing NDAs.</div>
<div>
<br /></div>
<h4>
Hired / Continued Involvement</h4>
<div>
In the even that you're hired, your relationship with the hiring agency may be short-lived or have a longer existence depending on whether the position is hourly (e.g. contractor) or direct.</div>
<div>
Assuming that you were hired as a direct (W2) employee, the agency involvement is nearly complete. The hiring company is obligated to pay a placement fee, typically a percentage of the positions annual salary. For instance, if you are hired at $100,000, the agency gets an X% placement fee of that $100,000. Likely, that placement fee is contingent on continued employment, like staying in the position for 3-months.</div>
<div>
<br /></div>
<div>
Contractors, or hourly workers, have a longer-term relationship with the agency. The contractor bills the agency $X / hr, the agency bills the hiring company $Y / hr (X > Y). Such long-term relations between you and your representing agency can be binding, preventing you from changing to an alternative agency for the same position. What if you love the contract job, and find the agency to be a nightmare? You are kinda stuck, for the duration of the position, learn to work with the agency or leave the position. A 3-month contract, 6-month, 1 year, 10 year.....you're pretty much stuck with the agency/position pairing. It's important to choose a good agency because it's equally difficult terminating an AOL subscription.</div>
<div>
<br /></div>
<h4>
Fire Me A Resume</h4>
<div>
Let's go back to the fork-in-the-road decision to send an agency for a job position. Hopefully it's clear from the process discussed above that this decision shouldn't be taken lightly. At this particular moment you are entering a relationship, possibly with a person/company you know absolutely nothing about. I'd implore you to take this step in an informed position. You are empowering this person to provide professional introductions and communications on your behalf. This person/company will be representing you for a bit, or a long while, and you may very well be working with them for a while. Work with respectable agencies and your professional life and reputation can be preserved. Agency specifics can also effect your selection probability. For example, an agency may bill you out significantly higher than other candidates a disorganized agency may prevent you from getting a position you're highly qualified for.</div>
<div>
<br /></div>
<div>
<h4>
Hiring Agency Compensations</h4>
</div>
<div>
Gaining insight into agency compensation specifics is difficult and perhaps unnecessary or uninteresting for many. Signed agreements legally constrain discussions and social conventions tend to carry the torch as well. I'm currently contracting and my hiring manager likely is unaware of what I bill my contracting agency hourly. My hiring manager knows what the agency is billing me out at, and only the agency has specifics of both rates. Tomorrow, if I chose to, I could walk in and tell my hiring manager my bill rate and then he and the agency would then know both rates. As an hourly worker it's difficult to ever learn what you are being billed to the company at. Having positions both as a contractor and at the hiring company, having reviewed agency agreements and knowledge of industry-norms of staffing agencies I can give you some insight. Your mileage will vary.</div>
<div>
<br /></div>
<div>
Let's start with the easiest; staffing direct (W2) agency compensation. Typically, a placed candidate is compensated by a one-time placement fee paid from the hiring company to the staffing agency. Typically, 20% of the annual salary of the new employee. Hire in at $100,000 and the agency gets a $20,000 check. I've seen the placement rate negotiated down to 18% and such, but typically 20% is pretty standard.</div>
<div>
<br /></div>
<div>
Hourly rates for contractors are compensated differently. The agency pays the contractor $X for each hour of work, and bills the company $Y. I've *heard* of agency adjustment rates as low as 15%, but more typically I've seen 40% adjustment rates. In one particular case, an agency billing $120/hr, the contractor sees $85/hr.</div>
<div>
<br /></div>
<div>
Why should you care? Well, it's important to know what you're getting for what you're indirectly paying for. This awareness should be considered when you ask for annual rate adjustments and such. It can also be of interest if you ever wish to transition from a W2 contractor to a C2C contract.</div>
<div>
<br /></div>
<div>
This rate adjustment is paying for agency services, administrative activities, the frequent on-site visits hustling for new job prospects, possible sick-time, vacation days, retirement accounts, professional liability insurance, legal consults, agreements,...so while at first glance it may seem extreme, understand it bankrolls a good deal of activities and expenses.</div>
<div>
<br /></div>
<div>
In addition, we are likely all accustomed to getting paid regularly, perhaps weekly, bi-weekly or monthly. Businesses have been known to be sluggish in payments and it's not uncommon to invoice monthly but be paid by net-30, net-45, net-60 conventions. That means, the company you're providing service to may only pay for those services 30/45/60 days later. An hour you work today, invoiced later, is paid 30/45/60 days later. Agencies buffer that payment in many cases, isolating the payment schedule from the company from the contractor. Contractor gets paid regularly, agency gets paid less frequently.....a short-term loan orchestrated by the agency acts as a buffer to the individual contractor.</div>
<h4>
Multiple Agencies</h4>
<div>
So, what is the harm in multiple agencies distributing your resume? Well, nothing completely, in fact it can be a good idea for a few reasons, but can have negative consequences as well.</div>
<div>
Let's hit on some pros first. Agencies are hustling for getting the inside track on job positions. Some, maybe many, are constantly visiting companies and asking "Do you have any new positions coming up?". Once an agency has a relationship with a hiring manager they will continue to reach out to them with the goal of finding out early of upcoming positions. That gives them additional time to stack up some potential candidates. In a fast-pace industry, many companies will fill a position soon after finding a good candidate. A queue of potential candidates presented to a hiring manager early increases the chance of being selected. Good agencies are constantly seeking new positions coming up rather than waiting for the position to be publicly posted.</div>
<div>
Additionally, some hiring companies have a pre-approved list of agencies that they work for. A job at company X which exclusively works with agency Y kind-of means you need to submit your resume through agency Y to get the job. </div>
<div>
<br /></div>
<div>
There are negative consequences of shotgun blasting your resume to each and every agency that approaches you. The first-come-first-serve representation model means that the first agency to submit your resume represents you, for now and possibly virtual-eternity. That agent could be a flake or a superstar; just perform your do dilligence. As a seasoned contractor I have numerous stories of staffing agency shenanigans that place you in financial and professional jeopardy, be wary and vet any and all representatives that will be representing you.</div>
<div>
<br /></div>
<h4>
Agency Expectations As A Hiring Manager</h4>
<div>
As a first-time hiring manager, one of the things I expected from a staffing agency that I later found to be unlikely is the ability of them to perform 1st stage vetting of technical expertise. I figured that software technical recruiters would be....well....modestly technical. You'll likely never find a recruiter with a degree in engineering, nor a graduate of a code camp, or even well-versed in informal knowledge from YouTube. I kinda expected technical recruiters to be more skilled in vetting than a general-purpose human resources representative, but that typically isn't the case. Staffing agencies often are representing multiple disciplines so it's unlikely they could acquire sufficient expertise in the relevant disciplines. Staffing agencies that <i>specialize</i> in engineering simply means they place engineers, it doesn't mean they have proficiency in recognizing engineering fundamentals. Keyword resume searches are about all you can expect by agency reviews. It's equally likely that the agent you are working with is a 20-year vet in technical recruiting <u>or</u> fresh out of college.</div>
<div>
<br /></div>
<div>
Our industry is perhaps overly accepting of the eccentric, genius programmer, or some on the spectrum. Distinguishing a good-intending socially awkward person from an a$$hole isn't always difficult but in my experience you don't tend to find an agency making any effort to rule out terrible team player. This is especially true from 'resume farm' agencies who simply collect resumes from anyone willing to provide them and shotgun them out to any/all job postings. These agencies are the equivalent to day-traders and are easily recognized with the modest of communications, my advise would be to simply stay away from them...period, they'll do you no favors.</div>
<div>
<br /></div>
<div>
Most of us have a list of references available to be delivered on-request, people we have worked with closely that can speak to what we are like as a professional. Additionally, every position we've ever had has co-workers and those co-workers know whether we are competent and if we're impossible to work with. Agencies typically don't talk to references nor past co-workers and do little more than just talk to the candidate. I originally thought that an agency participation would conduct such discussions, but I found this not performed by any agencies I've worked with. The burden is typically on the hiring company and dysfunctional candidates can hide in plain sight. </div>
<div>
<br /></div>
<div>
Background checks aren't provided by most agencies as well, that typically falls again to the hiring company, be wary of candidates posing false credentials as the agencies typically won't do it for you.</div>
<div>
<br /></div>
<div>
That said, your best use of an agency is simply providing them with a keyword search and have them open the faucet with potential candidates. Don't anticipate the agency has the knowledge, expertise or capacity to perform any real vetting. Prepare to review dozens, or hundreds, of resumes and prepare to completely manage the comprehensive list of candidates you've been given, who you've ruled out and who has been scheduled, disqualified......</div>
<div>
<br /></div>
<div>
Any agency that says they'll do it for you is misguided or only telling a partial truth. Unless you have an exclusive agreement with ONE agency you'll have to do it yourself. Whether it's two or twenty agencies sending candidates, the burden will be on you to manage the hiring workflow. Make a spreadsheet right out of the gate and keep track of the evaluation process stages. <br />
<br />
On the topic of a single-source exclusivity agreement with a staffing agency.....for the love of all that is holy DON'T DO IT. You may be enticed by an elaborate weave of reasons why <i>this particular staffing agency</i> can do it all for you, give you a sweet discount and shower you with daffodils and unicorns in a fairy-tale journey of finding the GOAT candidate. It's all fiction, you'll have a limited stream of potential candidates and quite likely be assigned a disorganized representative from the agency, locked in likely for a year via exclusivity contract; nightmare material.</div>
<div>
<br /></div>
<div>
I hope this finds some of you as interesting and/or informative. Unless you're a long-term employee who picked their full-life employer you'll likely conduct business with a staffing agency at some time in your career. Choose them wisely, they will make this process easier and more effective. Choose them poorly at your own peril. Interview them with the same diligence as you would the candidate or company you seek.</div>
<div>
<br /></div>
<div>
Cheers.</div>
Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-56645323356149571322021-03-13T20:37:00.005-08:002021-03-13T20:37:33.613-08:00Archiving Paper Records<p> As part of the 'quarantining housecleaning' I've found myself digging out old paper material from years past. Literally, hundreds of pounds of archives that have made their way hundreds of miles, through multiple residences only to gather dust for the past 15 years in the same location. These papers have proven useful on a rare occasion, just often enough to prevent me from simply tossing them in the recycling bin.</p><p>With a surplus of time available and limited ways to spend it I've spent the last days scanning these documents for archival. Terabytes are cheap, at the ready, and small in physical size....an obvious way to make a bit more room in the old office.</p><p>Single-sided archives in bundles that readily fit in the scanner hopper are easy, slide them in, hit scan and wait for them to complete. Double-sided archives and/or bundles too large for the hopper required scanning in smaller bundles and reassembling thereafter. I'll spend a bit of time on how I've accomplished such hurdles.</p><p>Ubuntu provides a couple PDF utilities that were used, in particular <i>pdfseparate</i> and <i>pdfunite</i>.</p><p><br /></p><p>Let's say you have a couple hundred page single-sided document and need to scan them in bundles [1..10]. These bundles can be assembled (or concatenated) into a final PDF as follows:</p><p><span style="font-family: courier; font-size: x-small;">$ pdfunite bundle01.pdf bundle02.pdf bundle03.pdf...bundleN.pdf final.pdf</span></p><p>The final pdf file is defined as the destination. The bundles, when provided in order, will be concatenated in order into the final destination. This ordering can be explicitly defined, but more than once trying to accomplish it via the following command has eaten my lunch.</p><p><span style="font-family: courier; font-size: x-small;">$ pdfunite `ls bundle*pdf` final.pdf</span></p><p>The <i>ls</i> command provides no guarantee of delivering the list in sorted order, and `ls | sort -n` can be fickle depending on how the file names are names, but reader beware, don't be surprised for files like bundle1* and bundle10 arriving adjacently and really screwing up your final page order.</p><p>What does however work well is asking <i>ls</i> to list the files in a single column ordered numerically by <i>ls -1v, </i>for example:</p><p>$ pdfunite `ls -1v bundle*pdf` final.pdf</p><p><br /></p><p>Double-sided material is a constant drag, especially for large stacks. The process, scan the front pages of the entire bundle first, then flip them and scan the back pages....in order. Then separate the front and back page PDFs in a way that staggers them and reassemble.</p><p><br /></p><p>It's not uncommon to have a 100+ page double-sided stack, we'll split them into 3 bundles, scan the front, flip them, scan the backs and reassemble, something like this;</p><p><span style="font-family: courier; font-size: x-small;">$ pdfunite SCN_0001.pdf SCN_0002.pdf SCN_0003.pdf front.pdf</span></p><div><p><span style="font-family: courier; font-size: x-small;">$ pdfunite SCN_0004.pdf SCN_0005.pdf SCN_0006.pdf back.pdf</span></p></div><div><div><span style="font-family: courier; font-size: x-small;">$ pdfseparate front.pdf page%d-01.pdf</span></div></div><div><div><span style="font-family: courier; font-size: x-small;">$ pdfseparate back.pdf page%d-02.pdf</span></div></div><div><br /></div><div>This sequence should result in N front-sourced pages (page*-01.pdf) <u>and</u> N back-sourced pages (page*-02.pdf) and out naming convention allows them to be listed front/back in order.</div><div><br /></div><div>We can reassemble as follows:</div><div><span style="font-family: courier; font-size: x-small;">$ psfunite `ls -1v page*pdf` final.pdf</span></div><div><br /></div><div>These utilities ease the effort, but two pages stuck together during any stage of the scanning can really ruin your night sifting through what page(s) are out of order.</div><div><br /></div><div>So far, I'm on my way to fill the 2nd 64-gallon recycling bin, making a good deal of room for future hoarding.</div><div><br /></div><div><br /></div>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-38842659380974230662021-03-13T20:37:00.004-08:002021-03-13T20:37:27.840-08:00Processing Large Quantities of Files with Find/Exec<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio4JtmVvZhYhneQ8K2knRZPHlXc_UkToWvR52PGJjt_bD2rIIXXkRE8KcOWjQI9uqpqetD3sazUzqUFeMY5nNHk06671z-JDEW-kMCCnBZd_1ltmzxK9jL049t-2GQ6XG5mmnIwesMhjsC/s2048/pexels-markus-winkler-4144768.jpg" style="margin-left: 1em; margin-right: 1em;"><img alt="Photo by Markus Winkler from Pexels" border="0" data-original-height="1365" data-original-width="2048" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio4JtmVvZhYhneQ8K2knRZPHlXc_UkToWvR52PGJjt_bD2rIIXXkRE8KcOWjQI9uqpqetD3sazUzqUFeMY5nNHk06671z-JDEW-kMCCnBZd_1ltmzxK9jL049t-2GQ6XG5mmnIwesMhjsC/w400-h266/pexels-markus-winkler-4144768.jpg" title="Photo by Markus Winkler from Pexels" width="400" /></a></div><br /> <p></p><p>I've always found the find command to be incredibly useful, but using with the exec command powerful but frustrating and confusing. Often, patience runs thin and rather than take the time to learn how to effectively use find/exec for complex problems I bunt, returning to creating a tailor-made one-off bash script. In the end, mission accomplished, but I always feel disappointed to have to revert to a custom bash script when I know in my heart-of-hearts its accomplishable quickly if I only knew how to do it.</p><p>Today is the day, I'm gonna spend some time to better understand how to use find/exec for some repeatedly necessary types of problems.</p><p>Let's start with an easy case, one that's less common, but really easy to accomplish. We'll build from there.</p><h3 style="text-align: left;">Copying Files To New File Name (Prepending/Appending) <br /></h3><p>Let's say you have a list of files that you want to rename by pre-pending, or appending a substring. For instance, say you have a hierarchy of directories with image files that you wish to copy to a *.backup filename;</p><p><span style="font-size: small;"><span style="font-family: courier;">$ find . -name "*.jpg" -exec cp {} {}.backup \;</span></span></p><p>The above command will find all *.jpg named files, for each file execute 'cp <filename> <filename>.backup'. In other words, when finding image01.jpg the exec command would be cp image01.jpg image01.jpg.backup. This would be done for every encountered file that satisfies the regex.</p><p>Prepending a string in a similar manner could be done by:</p><p><span style="font-family: courier;">$ find . -name "*.jpg" -exec cp {} backup-{} \;</span></p><p>In this case image01.jpg would be copied to backup-image01.jpg.</p><p>Simple, fast, but not particularly useful if you're particular about the destination file names.</p><h3 style="text-align: left;">Replacing File Extension </h3><p>A bit more practical scenario is to want to change file extensions. For example, say you really prefer *.jpg but have a series of files named *.jpeg.</p><p>This one is a bit trickier, takes a little more expertise, but can readily be accomplished and understood with a bit of time.</p><p><span style="font-family: courier;">$ find . -name "*.jpeg" -exec sh -c 'mv "$0" "${0%.jpeg}.jpg"' {} \;</span></p><p>The simple filename substitution (e.g. {}) just doesn't cut it like the previous example because we wish to manipulate the filename. So, we inline a shell command, one that is capable of using the incoming file name as is (e.g. $0) and able to manipulate it (changing .jpeg to .jpg). That's the brief, lets dig a bit into it to better understand what's going on.</p><p>Incoming filenames sent to the shell script will be called *.jpeg (guaranteed by the find regex). The filename comes in as a parameter (e.g. $0) to the shell script so the first 1/2 of the move command could be 'mv file01.jpeg ...'. It may be worth pointing out, those that author shell commands may be familiar with $0 being the script name and the first argument be $1, but for an inline shell script, the first argument will be $0 as we are using it.</p><p>How about the seconds 1/2 of the shell command; while it looks like Snoopy dropping curse words it genuinely is meaningful.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnAzrLqsio727L-Ai654SMf8tZrisud67TVZChQt21Nwr04y5hwc_8SKIfrx5zR2XyLsyRmGbn6m7_EleRgUncRnJ1WJ9q3SEyhqDUO7j-Bn_cedDOjNTeSjbRTa0enaQAN3PQ_RuAj_R1/s349/e9270e05a9b5a7959d9404d64e7953a2--cyber-bullying-stop-bullying.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="349" data-original-width="236" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnAzrLqsio727L-Ai654SMf8tZrisud67TVZChQt21Nwr04y5hwc_8SKIfrx5zR2XyLsyRmGbn6m7_EleRgUncRnJ1WJ9q3SEyhqDUO7j-Bn_cedDOjNTeSjbRTa0enaQAN3PQ_RuAj_R1/s320/e9270e05a9b5a7959d9404d64e7953a2--cyber-bullying-stop-bullying.jpg" /></a></div><br /><p>The "${0%.jpeg}.jpg" is comprised of two parts; the first part ${0%.jpeg} is a variable pattern substitution;</p><p style="margin-left: 40px; text-align: left;"><b class="COMMAND"><span></span>${var%Pattern}</b>
Remove from <tt class="VARNAME">$var</tt>
the <i>shortest</i> part of
<tt class="VARNAME">$Pattern</tt> that matches
the <tt class="REPLACEABLE"><i>back end</i></tt> of
<tt class="VARNAME">$var</tt>. </p><p style="margin-left: 40px; text-align: left;">refer to this for details: <a href="https://tldp.org/LDP/abs/html/parameter-substitution.html">https://tldp.org/LDP/abs/html/parameter-substitution.html</a> <br /></p><p>Simply put, take $0 (the filename) and grab everything up to .jpeg, image01.jpeg would be expanded to image01. <br /></p><p>The second 1/2 of the expression simply re-adds .jpg, so the whole expression of image01.jpeg would be image01.jpg. With the existing and new file names now available, pairing them with a mv command and you're in business.</p><h3 style="text-align: left;">Removal of Spaces in File Names</h3><p> Ugh, I'd rather step in dog shit barefoot than have spaces in my file names. I know, it's an irrational hatred but there it is. Filenames are the bane of scripting, while they can be addressed, a simple 3 line shell script quickly becomes immensely more complex when dealing with filenames w/spaces. But, like bedbugs, any exposure to the outside world likely will bring them into your system. So, you need to be prepared to either live with them or a quick means to remove them from the filenames. My latest headache was downloading a series of video files from a MOOC, resulting in files of the form 'index 1.mp4'. So, we will extend on our above example, but utilize bash (rather than sh) to gain some substitution features. The "${0/ /_}" (a space between the '/' pairs) means replace all instances of spaces with '_'<br /></p><p><span style="font-family: courier;"> $ find . -name "*.html" -exec bash -c 'mv "${0/ /_}"' {} \;</span><br /><br /></p><h3 style="text-align: left;">Massive Media Conversion in One Command</h3><p>While we've been focusing on shell scripts using 'cp' or 'mv' commands, we aren't limited to easy commands, let's say we wished to convert a hierarchical folder structure of AVI files that we want to reencode as MP4 files.<br /></p><p><span style="font-family: courier;">$ find . -name "*.avi" -exec sh -c 'ffmpeg -i "$0" -acodec copy "${0%.avi}.mp4"' {} \;</span></p><p>Cut that puppy loose on your computer and come back to a newly created list of MP4 files.</p><p> </p><p>Hope this helps some of you. I feel I understand the use of exec better having worked through this. Cheers.<br /></p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-17267718453143750872021-03-13T20:37:00.003-08:002021-03-13T20:37:21.396-08:00Dealing With Filenames Containing Spaces<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQh4f83hzc5xAipigy5CZKrZdDUKVArE6XH0mtRvtpg2WFBBS_WVES7A2rJ3PmM_GSp7xFgSvqRJ_ZBqhNBJ_VgdSwAvOSHPlXA-GUGYzdGiWgY3d98YhWDdF08OKmj3OZ3dZqoyvBOnFN/s2048/pexels-xxss-is-back-777001.jpg" style="margin-left: 1em; margin-right: 1em;"><img alt="Photo by XXSS IS BACK from Pexels" border="0" data-original-height="1365" data-original-width="2048" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQh4f83hzc5xAipigy5CZKrZdDUKVArE6XH0mtRvtpg2WFBBS_WVES7A2rJ3PmM_GSp7xFgSvqRJ_ZBqhNBJ_VgdSwAvOSHPlXA-GUGYzdGiWgY3d98YhWDdF08OKmj3OZ3dZqoyvBOnFN/w400-h266/pexels-xxss-is-back-777001.jpg" title="Photo by XXSS IS BACK from Pexels" width="400" /></a></div><br /> <p></p><p>Since the Internet is used by a variety of users, operating systems and naming conventions it's not uncommon to get filenames that contain spaces. Trouble is, this can wreak havoc with simple scripts that don't anticipate filenames with spaces.</p><div><br /></div><div>for example;</div><div>With a folder containing files like this:</div><div><div>lipeltgm@kaylee:/var/tmp$ ls *ts</div><div>Nokomis Track - Minnebar15 - Oct 10th – Crowdcast-TohCuy6dhE00Y02iSoGYDo75LAQVfEmKmk (1).ts</div><div>Nokomis Track - Minnebar15 - Oct 10th – Crowdcast-TohCuy6dhE00Y02iSoGYDo75LAQVfEmKmk.ts</div><div>Nokomis Track - Minnebar15 - Oct 13th – Crowdcast-Qcgrm8WtcFKjmHYGs00Bg01Hy2HkkmmZYs.ts</div><div>Nokomis Track - Minnebar15 - Oct 6th – Crowdcast-OmLjaiEpTLslbQ9GHLmKITTSagJ00COuy.ts</div><div>Nokomis Track - Minnebar15 - Oct 8th – Crowdcast-JyWZ7lzwvH5MZSuGvp2uE49DVpeCVO3r.ts</div><div>Phalen Track Backup - Minnebar15 - Oct 13th – Crowdcast-dozLLcdmIlG6Bqdx2i99eNO8giYjpYsN.ts</div><div>Phalen Track - Minnebar15 - Oct 10th – Crowdcast-lzz9BX00V8cswvC2csSvecrjV00w1o80201G.ts</div><div>Phalen Track - Minnebar15 - Oct 13th – Crowdcast-vod_master.ts</div><div>Phalen Track - Minnebar15 - Oct 6th – Crowdcast-m7ItulACMOv6a6jhQuswa1nwseStEsfj.ts</div><div>Phalen Track - Minnebar15 - Oct 8th – Crowdcast-3xMlXLl8kVgTBVmbw602c78acyLDJpLcM.ts</div></div><div><br /></div><div>A simple script, as follows, will interpret each entry as space/eol seperated. Yes, you can address it by changing the delimiter, but another option is to simply rename the files to something more expected.</div><div><br /></div><div>$ cat /tmp/go</div><div>for file in `find . -name "*.ts"`; do</div><div>echo $file</div><div>done</div><div><br /></div><div> The following find command will locate files with spaces in their names, changing the spaces to underscores ('_') to resemble something a bit easier to work with.</div><div><br /></div><div><span style="font-family: courier; font-size: x-small;">$ find . -type f -name "* *" | while read file; do mv "$file" ${file// /_}; done</span></div><div><br /></div><div>Hope this helps.</div>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-7091211965123215562021-03-13T20:37:00.002-08:002021-03-13T20:37:14.466-08:00How Do You Process Large Number Of Files with FFmpeg?<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjddzlYQvw95BfRm50rpICIocOOM8OhTskZbYlrJppinEutYDLm_ogngzf_LIMffAniNzS_oZFt5VsF1BiG6lC-7X-xuQMLsCYxrRU2Csj2fEj3nStUeWBkBwaIYOKRnk8wV6P30JO6ft7h/s2048/pexels-graham-walker-772278.jpg" style="margin-left: 1em; margin-right: 1em;"><img alt="Photo by Graham Walker from Pexels" border="0" data-original-height="1365" data-original-width="2048" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjddzlYQvw95BfRm50rpICIocOOM8OhTskZbYlrJppinEutYDLm_ogngzf_LIMffAniNzS_oZFt5VsF1BiG6lC-7X-xuQMLsCYxrRU2Csj2fEj3nStUeWBkBwaIYOKRnk8wV6P30JO6ft7h/w640-h424/pexels-graham-walker-772278.jpg" title="Photo by Graham Walker from Pexels" width="640" /></a></div><br /><p><br /></p><p>Suppose you have a large number of media files you wish to convert to an alternative container or format. This post will focus on how you author a single, specifying a FFmpeg command, to be run on all the files resulted from a find command.</p><p>You could absolutely author a quick bash file, feel free to do so, but you may find this is quicker and easier;</p><p>I'd recommend you take a peek at this post XXXX as it lays out the background on utilizing Find/Exec and leads up to the final command.</p><p>This command will find all AVI containers and reformat into an MP4 container. It shows the simplest of file conversions, it can be expanded as needed, perhaps to provide a consistent encoding scheme for all files in a subdir.</p><p><span style="font-family: courier;">$ find . -name *.avi -exec sh -c ffmpeg -i "$0" -acodec copy "${0%.avi}.mp4" {} ;</span></p><p><br /></p><p>For more details, refer to FFmpeg Official Site: <a href="http://ffmpeg.org/">http://ffmpeg.org/</a></p><p> </p><p>Hope this helps.</p><p>Cheers</p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0tag:blogger.com,1999:blog-4887609088553785394.post-2173986595594832692021-03-13T20:37:00.001-08:002021-03-13T20:37:08.256-08:00Making Videos Look Old Timey<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBQkSCmTL4_mGueJMsuHtQJlCxDu1VZGN0Af_tVS6D-drqnPBsH5dHvkK3HKnEa-_zpKNYaVEfCyiSAhEOlBdMIdFHDdLfiL6ZDo6Uc1OS5mBQnCqGRLKa7EX697mo9yvxoeH9b5DxISso/s2048/pexels-pixabay-46794.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1356" data-original-width="2048" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBQkSCmTL4_mGueJMsuHtQJlCxDu1VZGN0Af_tVS6D-drqnPBsH5dHvkK3HKnEa-_zpKNYaVEfCyiSAhEOlBdMIdFHDdLfiL6ZDo6Uc1OS5mBQnCqGRLKa7EX697mo9yvxoeH9b5DxISso/w640-h424/pexels-pixabay-46794.jpg" width="640" /></a></div><br /> <p></p><p>This <a href="https://ottverse.com/create-vintage-videos-using-ffmpeg/">post</a> recently came across my news feed and outlines a means to convert a modern video to look vintage or 'old timey'. This post will summarize the method and offer a makefile that performs the transformation.</p><p><br /></p><p>The general effect takes three steps: 1) downsample the frame rate, 2) color adjust to an older style, 3) grab a vcr noise overlay, scale it and apply it.</p><p><span style="font-size: xx-small;"><span style="font-family: courier;">$ cat -n Makefile <br /> 1 all: sideBySide.mp4<br /> 2 # https://ottverse.com/create-vintage-videos-using-ffmpeg/<br /> 3 <br /> 4 %.ts: %<br /> 5 ${SH} ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 $< > $@<br /> 6 <br /> 7 %.size: %<br /> 8 ${SH} ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 $< > $@<br /> 9 <br /> 10 input.mp4:<br /> 11 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=uvy02Cv7DIc<br /> 12 <br /> 13 adjustFps.mp4: input.mp4<br /> 14 ${SH} ffmpeg -i $< -filter:v fps=fps=10 -strict -2 $@<br /> 15 <br /> 16 vintageFilter.mp4: adjustFps.mp4<br /> 17 ${SH} ffmpeg -i $< -vf curves=vintage -strict -2 $@<br /> 18 <br /> 19 vcrOverlay.mp4: <br /> 20 ${SH} youtube-dl -f mp4 https://www.youtube.com/watch?v=J_MZb7qTenE -o $@<br /> 21 <br /> 22 overlay.mp4: vcrOverlay.mp4 input.mp4.ts vcrOverlay.mp4.ts input.mp4.size<br /> 23 ${RM} list.txt<br /> 24 ${SH} bash -c "for i in {1..$(shell echo `cat $(shell echo $^ | cut -f 2 -d ' ')` / `cat $(shell echo $^ | cut -f 3 -d ' ')` + 1 | bc)}; do printf \"file '%s'\n\" $< >> list.txt; done"<br /> 25 ${SH} ffmpeg -f concat -i list.txt -vf scale=$(shell cat $(shell echo $^ | cut -f 4 -d ' ') | sed -e "s/x/:/g"),setsar=1:1 -an $@<br /> 26 <br /> 27 video.mp4: overlay.mp4 vintageFilter.mp4<br /> 28 ${SH} ffmpeg -i $(shell echo $^ | cut -f 1 -d ' ') -i $(shell echo $^ | cut -f 2 -d ' ') \<br /> 29 -filter_complex "[0]format=rgba,colorchannelmixer=aa=0.25[fg]; [1][fg]overlay[out]" \<br /> 30 -map [out] -pix_fmt yuv420p -c:v libx264 -crf 18 $@<br /> 31 <br /> 32 sideBySide.mp4: input.mp4 video.mp4<br /> 33 ${SH} ffmpeg -y -i $(shell echo $^ | cut -f 1 -d ' ') -i $(shell echo $^ | cut -f 2 -d ' ') \<br /> 34 -filter_complex '[0:v]pad=iw*2:ih[int];[int][1:v]overlay=W/2:0[vid]' -map [vid] \<br /> 35 -map 0:a -acodec copy -shortest $@<br /> 36 <br /> 37 clean:<br /> 38 ${RM} *.mp4 *.ts list.txt *.size<br /></span></span></p><p><br /></p><p>The end result should give you something of the form of this:</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dw_3WEpPJkf9wARV1gfwS-N22lhgpj_gxtdRWHfOrFedZiGY2LIvLVCgHKbH3FK_yGEhrf3lGABSWQJM_CpNQ' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><br /><p><br /></p><p>For more details, refer to FFmpeg Official Site: <a href="http://ffmpeg.org/">http://ffmpeg.org/</a></p><p> </p>Grant Lipelthttp://www.blogger.com/profile/01352348996640352756noreply@blogger.com0