I wanted to be able to access my Raspberry Pi filesystem from another machine to take advantage of more processing power elsewhere. It’s been many years since I’ve used NFS, and while it was easy to add to the Pi, I ran into a couple of issues that took some time to work out.
The primary use I was trying was to be able to mount the filesystem from a Debian instance of Linux running on Windows Subsystem for Linux (WSL) on my Microsoft Surface Pro 9. This would allow me to run FFMPEG working on some video files utilizing the power of the modern Intel chip instead of waiting for the Pi ARM processor to process the files.
While I was able to get the filesystem mounted from a second Pi easily, the WSL instance was more complicated. I realized that the problem is that by default WSL instances use Network Address Translation (NAT) for network access. This required the “insecure” option on the NFS server setting because NAT was changing the ports it was using to port numbers above 1024.
sudo apt install nfs-kernel-server -y
Edit the /etc/exports file to export the directory I wanted and set who has access to it. I’m limiting to my local network IP addresses, and not specifying any security. Because of my lack of security, I’ll be removing all of this after I am finished with the processing. Note the insecure tag at the end of the options.
Use the command sudo exportfs -ra to force the NFS server to reload its configuration file.
Use the command showmount -e to display what filesystems have been exported.
I use Microsoft Visual Studio as my primary development environment. I’ve got a long-term repository that I maintain on Azure Devops at https://wimsworld.visualstudio.com/. When I create a new test project, I often create it as a subdirectory inside the main repository. That gives me version control, without the hassle of creating a new repository every time.
The downside is that when a project grows into a real program, I want to make public, I have to figure out what to do about the code. I could take a snapshot, losing all of the history, or I could jump through hoops to get the project exported from the Azure git platform and imported into the GitHub platform. I like the history of how I got from the start to where I am, so I chose to go thourgh the hoops.
I should describe this with examples. My default code repository is WimsWorld and I often create projects as directories of that, so WimsWorld/WimsConstructionCam in this case. I created this project in July, and then a couple of months later I realized I should have created the program in WimsWorld/Linux/WimsConstructionCam, so I moved it one level deeper in the repository. Git handles all of that fine and I could trace back the history of the file all the way back to creation.
Now that it’s time for me to decide I may be interested in sharing this project publicly, I looked for step by step instructions and came across a nice video doing what I wanted.
This was easiest to do on a linux box at the command line, and since I have a dev system that has its SSH keys already registered as me in both Azure Devops and GitHub I could repeat the process multiple times to get it right.
The git –filter-branch –subdirectory command got rid of all of the history except for what was in the Linux/WimsConstructionCam/ directory, which seemed right, since all the files I wanted history on currently lived in that directory.
I had previously created a repository in GitHub and this got what appeared to have history in GitHub. On closer inspection, the history only went back a couple of months, and not back to when I’d created the project. I deleted my project in github, deleted the repository directory on my local machine and started again after some research.
First I tried to manually delete everything I didn’t want from my cloned repository, move all the files to where I wanted, commit them, then connect to the new repo and push. I got a problem message about a file that was too large. Sometime in my past history, I’d managed to push a Visual Studio debug symbol database into my repository and while I’d deleted the file, I’d not deleted the history of the file from the repository. That issue was actually a good thing because it confirmed to me that method of getting the files looking the way I wanted could be leaking credentials that I might have somewhere in my private repository.
I found that I needed the git tool git-filter-repo to do what I needed to do.
git clone WimsWorld@vs-ssh.visualstudio.com:v3/WimsWorld/WimsWorld/WimsWorld
git remote rm origin
python3 ~/git-filter-repo --force --path WimsConstructionCam --path Linux/WimsConstructionCam
git mv * ../..
git commit .
mv WimsWorld WimsConstructionCam
git remote add origin firstname.lastname@example.org:wcbonner/WimsConstructionCam.git
git pull origin main --allow-unrelated-histories
git push origin master
Using the git-filter-repo program I was able to remove the history of everything that was outside of the original folder and the current folder. Then I moved all of the files from the current subdirectory to the repository root and committed the changes. Then I connected the repository to the new remote repository, pulled the remote, and then pushed to the remote.
A funny issue I ran into during my second try into a brand-new repository in GitHub. I’d originally created an empty repository a few weeks ago and the default branch was called master, which matched the default branch on my old repository. They’ve changed to use the current politically correct term for the default branch, and it’s named main. After I’d got both branches in GitHub, I had to merge the two so I’m working from a single branch.
I recently upgraded my drone to a DJI Mavic Air 2. Among other things, it can create h.265 videos directly. It still uses the MP4 container format and the separate SRT format for storing video subtitles, the flight data. Following most camera standards, it creates video files that are individually smaller than 4 GB, which works out to be about five minutes in 4k video.
If I want to upload a longer raw video to social media, the video files need to be concatenated before uploading.
Concatenating the video with FFMPEG has been something I’ve known how to do for a long time using either of two methods. Today I learned how to properly embed the subtitles in either the MKV or MP4 container format.
The MP4 format is more widely supported than the MKV format, but is less flexible as to what it can contain. The MKV (Matroska Multimedia Container) container format can hold almost any type of media, and so I’m able to copy the SRT format directly. The MP4 (MPEG-4 Part 14) container format only supports a limited selection of subtitle formats, so I’m required to have FFMPEG convert the SRT stream to a MP4 compatible stream. If you are interested in video container formats, these tables are very helpful.
I’ll give several examples using the two video files and their associated subtitle files created by the drone named DJI00001.MP4, DJI00002.MP4, DJI00001.SRT, and DJI00002.SRT. The method I’m using should work for any number of files, up to the largest filesize you can store on your filesystem.
To simply concatenate the video files, create a text input file (I’m using mp4files.txt) with the contents as follows
then use the ffmpeg command to create a new concatenated file.
When playing the ConcatenatedVideo files on my local machine, I can now enable or disable the closed caption track properly in the player for either format. Unfortunately in my initial testing with YouTube, neither format maintains the second stream of subtitles.
This is not all a waste of time and effort, because an advantage of embedding the subtitles into the container format is that the timing has been matched to the video, and can now be extracted in a concatenated form for use with YouTube.
You can exclude the “-c copy” when extracting the subtitles and FFMPEG fill run it through its subrip codec and produce nearly identical results. It will only work with the MKV file because the subtitle format stored in the MP4 file is not easily converted to a SRT file.
Using the -f concat option invokes the concat demuxer in FFMPEG, which has the limitation that the format needs to be exactly the same for each file. If there are any changes between files you want to concatenate, you must use a more involved command invoking the concat filter. In a different project I ran into a command issue with the concat filter command when the command got to be much over 900 characters long.
I wanted a quick and dirty method to test my camera module installation on my Raspberry Pi ZeroW installation. I don’t have a monitor connected to the Raspberry, and explicitly did not install the desktop version of the operating system. This is especially important because the camera itself may not be properly focused after installation in the case, and the only way to easily focus the camera is with a video stream allowing you to make small adjustments and see them nearly real time.
I’ve used FFMPEG for years as it handles almost any kind of video or audio I can throw at it. I use VLC on my desktop machine for similar reasons.
I did a quick install of ffmpeg on my Pi with the following command, allowing it to install all the requirements, adding up to almost 126 new packages and 56MB that needed to be downloaded and installed.
sudo apt-get install ffmpeg -y
After it finished installing, I was able to run the following command with the 192.168.0.16 address being my desktop computer.
On my desktop computer I ran VLC, under the Media menu, selected Open Network Stream, and opened:
What I’m doing is to use FFMPEG to pull video from the device and push it using UDP datagrams at my desktop on port 5000. Then VLC opens a port on the local machine at port 5000 to receive the datagrams and it decodes and displays the video. An interesting thing about this method is that I can stop transmitting from the raspberry, then restart it, and VLC will accept the packets since UDP is a connectionless protocol.
What really surprised me was that when I logged in a second time to my Raspberry Pi to view the CPU usage for streaming, it was only running around 12% of the CPU. I was interested in knowing what native formats the camera supported..
That output leads me to believe that the camera module could output either h264 or mjpeg without significant CPU overhead. What it doesn’t do is tell me efficient frame sizes. It seems to say that horizontal and vertical sizes can be anything between 32 to 3280 and 32 to 2464. I know that the specs on the camera say that it will run still frames at the high resolution, but video is significantly less.
Two Video4Linux commands that return interesting and similar results are:
I recently purchased a dedicated dashcam on sale to replace my GoPro setup for trip videos. This gives me a new need to understand a new file format.
The Roav Dashcam stores sequential mp4 files. When configuring the camera it’s possible to set the loop time, which is the duration of each mp4. There’s also an option to watermark the files. I have it turned on, and the only thing I’ve noticed is the ROAV logo, timestamp, and speed in the bottom right. It does not appear to have a way of adjusting the size of the text.
My initial recordings were set to run at 1080p 60 fps. I wanted to concatenate multiple files, add some text of my own, and speed up the video. This was my first experience using the -filter_complex option of FFMPEG. Here’s what I came up with to put together three files, speed the output up by a factor of 60, and add some text. I’m dropping the audio completely. The ROAV can record audio inside the car, but I configured it not to, as I don’t want to hear what I was listening to on the radio or what I might be saying if I make a phone call..
This first video was recorded at 1080p60. The camera can record at 1440p30 which I will be trying soon to see if things like license plates are more legible. The setpts factor that I’m currently using was 1/60, so that 1 minute of real time was compressed to 1 second of video, and just dropping the extra frames. I expect to need to change the setpts factor to 1/30 because of the decreased frame rate at the higher resolution.
I’ve noticed that YouTube transcodes my videos after I upload them and wanted to know more. It turns out that they are internally using a form of h.265 video encoding, which reduces the data size significantly without reducing perceived quality over h.264 video compression.
I decided to run some tests using my GoPro time lapse program to see how much compression I’d get versus how much extra time for encoding.
First I had to read up on the settings for using h.265 in FFmpeg. According to https://trac.ffmpeg.org/wiki/Encode/H.265, If I add -c:v libx265 into my existing FFmpeg command line without changing anything, I’ll get an h.265 output with the defaults of -crf 23 and -preset medium.
The -preset value effects how much work is done in the compression, but shouldn’t affect the perceived quality. It will effect both time to create and output file size.
The -crf value effects the perceived quality. I’ve been using the defaults in my previous h.264 mp4 files, which should be approximately -crf 23 and supposedly -crf 28 in h.265 is equivalent to the lower h.264 value. A -crf 0 would be a completely lossless conversion. For my tests, I left crf at the default 23.
I ran all of these tests on a set of 7,129 photos I’d captured while sailboat racing on March 24th using my GoPro Hero 3+ Black. Each input image was 4000×3000 and I’m creating an output video with resolution 3840×2160 by cropping it at 3/4 of the height and scaling to fit.
The original h.264 conversion took 28:36 minutes to create and was 1,162,079,866 bytes long.
Here’s a trimmed down and sorted file listing. The first column is the time it took to create, second is filesize, and third is filename.
I created a single test at -crf 20 because I was interested in seeing a higher quality video and how different it would be in size. It took slightly longer than the original h.264 compression and had a slight improvement in size.
Two things became obvious to me from this test. More time spent in compression doesn’t always mean better compression. For my application, running preset medium actually hurts the performance, both in file size and time taken.
I ran all of these tests only a single time on my desktop computer with an Intel(R) Core(TM) i7-4771 CPU @ 3.50GHz, 3501 Mhz, 4 Core(s), 8 Logical Processor(s) running the latest version of Windows Version 10.0.16299 Build 16299. I was running FFmpeg version N-88668-g723b6baaf8 from https://ffmpeg.zeranoe.com/builds/. The variations in time could be related to background tasks running on the machine, and I should have run a more comprehensive battery of tests, averaging time and with more accurate timekeeping.
I had been wanting the time-code from when each picture was taken printed on the screen while the video was playing but had not figured out how to get it done until this weekend.
Frame from video showing the DateTimeOriginal timecode embedded.
I’d gone down multiple paths in an attempt to get this result before finally getting the drawtext feature to work. My program manually pulled the metadata from the images before feeding them to ffmpg. I’d tried creating both text files and image files for overlaying. none of those got the result that I was looking for.
When I finally got everything working, it seems simple, but the underlying problem has to do with the amount of string escaping required to get the command to work.
Here’s an example command I was issuing to ffmpeg that got the result I was looking for.
If you look at the -vf option parameter, I’m cropping my input pictures to 3/4 their original height, then using the drawtext feature twice. First I write the static text to the bottom right of the frame, then I extract metadata from the source image and write it to the bottom left of the frame.
Because I’m calling this from a program, I had extra escaping of the \ character in my code. All of the escaping required a lot of trial and error to get things working. I’m using OCRAEXT as my font, but I could be using any fixed spacing font. because of the fact that the time is changing every frame, it’s important that the font not be proportional to make it easy to read.
I purchased a GoPro Battery BacPack recently because I realized that I’d rather have extended shooting than have two seperate batteries that needed to be charged. I had already purchased a second standard battery for the GoPro, but I didn’t have a way to charge it when it was not in the camera, so found it less useful than I was hoping.
The fact that I am most often using my GoPro in harsh conditions means that I’d rather not open the case any more frequently than I need to. When I’m skiing, if I go into the lodge to change the battery, the very first thing I notice is that the cold GoPro case is suddenly steamed over by the indoor humidity.
When I’ve been doing my stop motion photography at a picture every two seconds, generally the standard battery lasts just under two hours. My first test with the BacPac attached started at 8:47am and the last picture was at 12:40, so it looks like It gets me to just under 4 hours total.
It created 6999 files in that time frame. I’ve not figured out if the GoPro uses any less battery when taking sequential still photos vs when it’s taking movies.
Saturday’s race on Different Drummer wasn’t fully captured in the time allotted because we went out early and did some practice work flying the spinnaker.
Hopefully I’ll get around to writing more about what comes with the BacPac in the next couple of days. I was mostly interested in sharing the extension of the recording time.
In November 2013 I purchased a GoPro HERO3+ Black Edition to play with video recording, but almost immediately became enthralled with taking sequences of photos over long time periods.
The GoPro can be configured to take a picture every 0.5, 1, 2, 5, 10, 30, or 60 seconds. I’ve found that I like taking pictures every 2 seconds, and then converting them to video at 30fps (Frames per second) which gets me an easy to convert time scale. 1 second of video came from 1 minute of photos, 1 minute of video came from 1 hour of photos.
GoPro has a freely available software package to edit videos, as well as creating videos from sequences of images. Because of my past familiarity with FFMPEG I wanted a more scriptable solution for creating videos from thousands of photos.
https://trac.ffmpeg.org/wiki/Create%20a%20video%20slideshow%20from%20images has nice instructions for creating videos from sequences of images using FFMPEG. What it glosses over is that the first image in the sequence needs to be numbered zero or one. Another complication in the process is that the GoPro uses the standard camera file format where no more than 1000 images will be stored in a single directory. This means that with the 1800 images created in a single hour, at least two directories will hold the source images. An interesting issue I ran across is that sometimes the GoPro will skip a number in its image sequence, especially when it has just moved to the next directory in sequence. This is why I had to write my program using directory listings as opposed to simply looking for known files.
The standard GoPro battery will record just about two hours worth of photos. If the GoPro is connected to an external power supply, you can be limited only by the amount of storage space.
Here’s yesterday morning’s weather changing in Seattle.
Here’s a comparison of cropping vs compressing the video. I took this video on a flight from Seattle to Pullman last weekend. You can see much more of the landscape in the compressed version, and see that the top of the propeller leaves the frame in the cropped version.
I’ve written a program that takes three parameters, copies all of the images to a temporary location with an acceptable filename sequence, runs FFMPEG to create a video, then deletes the temporary images. The GoPro is configured to take full resolution still frames, 4000×3000, and I convert those to a 1080p video format using FFMPEG. Because the aspect ratio is different, and the GoPro uses a fish eye lens to begin with, both vertical and horizontal distortion shows up. I run FFMPEG twice, once creating a compressed video and a second time creating a cropped video. This allows me to chose which level of distortion I prefer after the fact.
The three parameters are the video name, the first image in the sequence, and the last image in the sequence. I am currently doing very little error checking. I’m presenting this code here, just to document what I’ve done so far. If you find this useful, please let me know.
I’ve been creating time lapse videos using FFMPEG from the output of a GoPro camera since the beginning of summer. I have always been interested in the output but not had easy methods of creating them until recently.
I like the result best when I have the GoPro set to take one image every 5 seconds, and then I have FFMPEG create a default MP4 file at 25 frames per second. The standard GoPro battery seems to record just about two hours worth of full resolution images in my Hero 3+ Black, which works out to just about a minute of video.
My first video using this method was of the sunset over Elliott Bay taken through the window of the elevator waiting room where I live, on the 13th floor.
I’ve written a program that copies the default GoPro image file names to a sequence that starts with image number 0000 so that FFMPEG will recognize a starting sequence with the default globbing method.
That command line will actually create a video that has a resolution of 4000×3000, which is the resolution I’m taking the individual pictures. I could have specified to FFMPEG to reshape the output, or trim it.
The second video I created using this method was of a sunrise in essentially the same location.
The most recent video was created after I purchased a suction cup mount for my GoPro. I went to the 17th floor and read a book during the hour before sunset.
Several things are apparent in this process.
I need a black out curtain surrounding the camera to block reflections when the light is directed at the camera. The camera has been positioned flush against the surface of the glass, but the thickness of the glass creates reflections inside the glass itself.
videos with weather are much more interesting than just the movement of the sun itself.
I have been turning on the WiFi in the camera to make sure I’ve positioned the camera correctly. I need to try turning off the WiFi to see how it affects the battery rundown length.
I need to change the picture frequency to a longer or short time-span to see if the battery life is affected at all.