Downsampling / Filtering Data Stream with FFMPEG - ffmpeg

We have a .ts input file that contains (among other streams) a video stream and MISB 0604-compliant KLV data stream. The output of ffprobe for these stream are:
Stream #0:0[0x111]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc
...
Stream #0:2[0x1001]: Data: klv (KLVA / 0x41564C4B)
We are hoping to extract every Nth frame of the video as a .tiff. We also need to associate each of these frames with a corresponding KLV packet from the MISB 0604-compliant data stream.
The following command that select filters and adjusts the original FPS by the corresponding ratio does result in the expected frames being saved out as TIFF (in this case the original video has 1187 frames, and I expect to get 12 frames from the select filter).
ffmpeg -y -i 2205Z.ts -map 0:0 -vf "select='not(mod(n,100))'",fps=30000/1001/100 -compression_algo raw -pix_fmt rgb24 %05d.tif
However I can't seem to get any filters working on the data stream. For example using filter:d does not throw an error, but also doesn't seem to actually filter. My question is whether ffmpeg can be used to save out a "downsampled" data stream corresponding to the downsampling operations performed on the video stream above?

Using a recent git master build, run
ffmpeg -i 2205Z.ts -map 0:2 -bsf "noise=drop=mod(n\,100)" -f segment -segment_format data -segment_time 0.01 %d.bin
The noise bsf uses an expression to drop, not select i.e. select=EXPR === drop=not(EXPR).
Add -loglevel verbose to see details about which packets are kept, in a format like this:
[noise # 000001cd418a68c0] Stream #2 packet 1099 pts 3420417 - amount 0 drop 1
[noise # 000001cd418a68c0] Stream #2 packet 1100 pts 3423419 - amount 0 drop 0
[noise # 000001cd418a68c0] Stream #2 packet 1101 pts 3426423 - amount 0 drop 1

Related

Ffmpeg: 4K RGB->YUV realtime conversion

I'm trying to use Ffmpeg for creating a hevc realtime stream from a Decklink input. The goal is high quality HDR stream usage with 10 bits.
The Decklink SDI input is fed RGB 10 bits, which is well handled by ffmpeg with the decklink option -raw_format rgb10, which gets recognized by ffmpeg as 'gbrp10le'.
I have a Nvidia pascal-based card, which supports yuv444 10 bit (as 'yuv444p16le') and when when using '-c:v hevc_nvenc' the auto_scaler kicks in and converts to 'yuv444p16le', which I guess is the same conversion as giving '-pix_fmt yuv444p16le'.
This is working very well in 1920x1080 resolution, but in 4096x2160 resolution ffmpeg can't keep up realtime 24 or 25 fps, and I get input buffer overruns.
The culprit seems to be the RGB->YUV conversion in ffmpeg swscale because;
When piping the Decklink 4K RGB input with '-c:v copy' straight to /dev/null, there's is no problems with buffer underruns,
And when feeding the Decklink YUV and giving '-raw_format yuv422p10’ (no YUV444 input for decklink seems available for decklink in ffmpeg) I get no underrun and everything works well in 4K. Even if I set '-pix_fmt yuv444p16le'.
Any ideas how I could accomplish a 4K hevc in NVENC with the 10-bit RGB signal from the Decklink? Is there a way to make NVENC accept and use the RGB data without first converting to YUV? Or is there maybe a way to convert gbrp10le->yuv444p16le with cuda or scale_npp filter? I have compiled ffmpeg with npp and cuda, but I cannot figure out if I can get it to work with RGB? Whenever I try to do '-vf "hwupload_cuda"', auto_scaler kicks in and tries to convert to yuv on the cpu, which again creates underruns.
Another thing I guess could help is if there was a way to make the swscale cpu filter(or if there is another suitable filter?) use multiple threads? Right now it seems to only use one thread at a time, maxing out at 99% on my Ryzen 3950x (3,5GHz, 32 threads).
Example ffmpeg output:
$ ffmpeg -loglevel verbose -f decklink -raw_format rgb10 -i "Blackmagic Card 1" -c:v hevc_nvenc -preset medium -profile:v main10 -cbr 1 -b:v 20M -f nut - > /dev/null
--
Stream #0:1: Video: r210, 1 reference frame, gbrp10le(progressive), 4096x2160, 6635520 kb/s, 25 tbr, 1000k tbn, 1000k tbc
--
[graph 0 input from stream 0:1 # 0x4166180] w:4096 h:2160 pixfmt:gbrp10le tb:1/1000000 fr:25000/1000 sar:0/1
[auto_scaler_0 # 0x4168480] w:iw h:ih flags:'bicubic' interl:0
[format # 0x4166080] auto-inserting filter 'auto_scaler_0' between the filter 'Parsed_null_0' and the filter 'format'
[auto_scaler_0 # 0x4168480] w:4096 h:2160 fmt:gbrp10le sar:0/1 -> w:4096 h:2160 fmt:yuv444p16le sar:0/1 flags:0x4
[hevc_nvenc # 0x4139640] Loaded Nvenc version 11.0
--
Stream #0:0: Video: hevc (Rext), 1 reference frame (HEVC / 0x43564548), yuv444p16le(tv, progressive), 4096x2160 (0x0), q=2-31, 2000 kb/s, 25 fps, 51200 tbn
--
[decklink # 0x40f0900] Decklink input buffer overrun!:02.52 bitrate= 30471.3kbits/s speed=0.627x

Getting the frame pkt_pts from ffmpeg

I record a stream similar to this (but I pipe the image to stdout and process the frames in real time):
ffmpeg -debug_ts -y -i http://my_stream.com -strict -2 out.mp4
Now I need to map my frames to the pkt_pts. This pkt_pts I can get by running:
ffprobe -i http://my_stream.com -show_frames -select_streams v:0
Although starting ffprobe is not an option since I need to excactly know which pkt_pts belongs to what frame.
Therefore I'm using the -debug_ts in my ffmpeg call, which gives me a lot of output to stderr, which I then parse out. The only output line which matches the number of frames is the one which starts with: muxer <- type:video This line does contain a pkt_pts, but it always starts with 0 (not the global pkt_pts).
Is there a way to map a frame, to the global pkt_pts in real time with ffmpeg?
You can run your original command, and extract lines starting with
demuxer -> ist_index:X
where X is the index number of the stream you want.
In each line, e.g.
demuxer -> ist_index:12 type:video next_dts:10851838034 next_dts_time:10851.8 next_pts:10851705034 next_pts_time:10851.7 pkt_pts:976665480 pkt_pts_time:10851.8 pkt_dts:976665480 pkt_dts_time:10851.8 off:0 off_time:0
-pkt_pts divided by stream timebase, 90000 for TS input, will give you full precision pkt_pts_time. If you don't know the timebase, see the input dump for your stream
Stream #0:0: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 768x432, 29.97 tbr, 90k tbn, 59.94 tbc
and use the tbn value.
Be aware that your ffprobe command selects the first video stream, but your ffmpeg command leaves it to ffmpeg to select streams, which may not select the same streams.

Trying to tonemap 14-bit grayscale video

I'm trying to generate h.264 video from raw 2-byte gray video (14-bit range encoded in 16-bit values). I can do something like:
ffmpeg -f rawvideo -pix_fmt gray16le -s:v 1280x720 -r 60 -i input.raw -c:v libx264 output.mp4
And I get video but it's pretty dark, not sure if it's clipping, doing a linear remap, or storing the 16-bit data and VLC is doing the remap. ffprobe is reporting Video: h264 (High 4:4:4 Predictive) (avc1 / 0x31637661), yuvj444p(pc), 1280x720, 108 kb/s, 60 fps, 60 tbr, 15360 tbn, 120 tbc
I was figuring I'd use the tonemap filter to make a better mapping. I added a filter before the output file with -vf.
tonemap=hable errors Impossible to convert between the formats supported by the filter 'graph 0 input from stream 0:0' and the filter 'auto_scaler_0'
zscale=transfer=linear,tonemap=hable errors Impossible to convert between the formats supported by the filter 'Parsed_tonemap_1' and the filter 'auto_scaler_1'
zscale=transfer=linear,tonemap=hable,zscale=transfer=bt709,format=yuvj444p errors code 3074: no path between colorspaces
I'm not sure where to proceed from here...

Re-encoding vlc-created mpeg2 .ts file results in 20 second file; AKA: multi-stream file with hidden streams

I was recording something with vlc off v4l2 (in case that makes a difference), and I just selected the first format that worked, being mpeg2 using TS container. File resulted in .ts extension, as automatically selected by vlc. When I then tried to put the video file in my video editor, it said the video was 19,884 hours long, when it should be about 6 minutes (it is ~80mb in size). When I try to play it in xine, it correctly shows the duration (vlc doesn't), and when I use ffprobe:
[mpegts # 0x9b2c0a0] max_analyze_duration 5000000 reached at 5000000
Input #0, mpegts, from 'loopbacktestcap.ts': Duration: N/A, start:
17978.139456, bitrate: N/A Program 1
Stream #0:0[0x44](): Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p, 640x480 [SAR 1:1 DAR 4:3], 104857 kb/s, 30 fps, 30
tbr, 90k tbn, 60 tbc
Stream #0:1[0x46](): Video: mpeg2video ([2][0][0][0] / 0x0002), 90k tbn
Notice especially this line:
Duration: N/A, start: 17978.139456, bitrate: N/A Program 1
I looked it up and it seems the lack of duration has to do with the container. But I have tried a few things to reencode (I tried -vcodec copy, mpeg2, libx264...) and all I can get is 20 second files of 1.1mb - 1.8mb.
So how can I reencode this file so the duration appears, and I get the full 6 minutes, instead of just the first 20 seconds?
First milestone was reached when I found this link: Map - ffmpeg -- Example #8 which led me to try:
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts
Yielding the following:
[mpegts # 0x9980f40] max_analyze_duration 90000000 reached at 90000000
Input #0, mpegts, from 'loopbacktestcap.ts':
Duration: 00:16:00.96, start: 17978.139456, bitrate: 695 kb/s
Program 1
Stream #0:0[0x44](): Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p, 640x480 [SAR 1:1 DAR 4:3], 104857 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc
Stream #0:1[0x45](): Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p, 640x480 [SAR 1:1 DAR 4:3], 104857 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc
Stream #0:2[0x46](): Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p, 640x480 [SAR 1:1 DAR 4:3], 104857 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc
Notice that it now gives me the correct duration as well as an extra stream and more info on the second one (which is technically the third one). So then I ran:
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0 -c copy map0.mp4
and that now produced a 79451050 byte file map0.mp4, where my_mpeg2_file.ts is 83499636. I try to play this in vlc, and it shows the duration as 9:12, but if I jog the play position, two more vlc windows are opened and vlc begins to act strange; the video display area is hung, though the playback position indicator continues to progress. This might be just a buggy vlc, but I am stuck at this point. Let me know if anyone sees anything I am missing here.
xine however cannot play back the new file (where it plays the original and shows that it is 6 minutes long -- actually, the duration counter goes a little nutty as I play the file and changes constantly, so I don't know).
So, next command I tried was:
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0 -c libx264 map0x264.mp4
Trying to play the resulting file (43652975 bytes, for the curious) led to the same strange behavior in vlc, where now I can see that it is opening a new window for each stream, and playing the stream when it is its "turn" in its respective window, freezing the other display areas of the other windows. Trying to close them stops playback for all and closes the two extra windows. I am guessing keeping all 3 streams in the same file is nonsense.
SUCCESS!
Next attempt was:
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0:0 -c copy map0_0.ts
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0:1 -c copy map0_1.ts
ffmpeg -probesize 90M -analyzeduration 90M -i my_mpeg2_file.ts -map 0:2 -c copy map0_2.ts
which resulted in files of size:
4912 map0_0.ts
5372 map0_1.ts
74728 map0_2.ts
map0_0.ts is 20 seconds long, map0_1.ts is a still image 1:12 long, and map0_2.ts is 9:12. Exactly what I wanted! And my video editor accepts them no problem. SOLVED!
I had a similar challenge of supporting transport stream (ts) video files in our video processing infrastructure. I used ffmpeg to convert ts into mp4 files with two-pass encoding.
The command(s) in its full length:
Pass 1:
ffmpeg -i camera.ts -filter:v scale=-1:480,setsar=1/1 -pix_fmt yuv420p -threads 0 -r 25/1 -force_fps -c:v libx264 -profile:v baseline -preset slow -x264opts level=3.0:ref=1 -b:v 1000k -maxrate 1000k -bufsize 2000k -s hd480 -c:a libfaac -ar 16000 -ac 2 -ab 128000 -pass 1 -movflags faststart -y video.mp4
Pass 2:
ffmpeg -i camera.ts -filter:v scale=-1:480,setsar=1/1 -pix_fmt yuv420p -threads 0 -r 25/1 -force_fps -c:v libx264 -profile:v baseline -preset slow -x264opts level=3.0:ref=1 -b:v 1000k -maxrate 1000k -bufsize 2000k -s hd480 -c:a libfaac -ar 16000 -ac 2 -ab 128000 -pass 2 -movflags faststart -y video.mp4 >>& ffmpeg.log
I downscale the video to hd480 (852x480) reencoding the video stream with libx264 baseline level 3 with a relatively low bitrate as well as audio with libfaac and moving the moov atom to the beginning of the file for web compatibility (-movflags faststart)
See the FFMpeg documentation for all details on the flags.
Note that I just dug up this command which works for our setup: processing transport stream recordings from IP cameras. You would probably need to specifically adapt resolution and bitrates to your needs. Hope this points you in the right direction.

ffmpeg - extract subtitles (which are unencrypted) when video is encrypted

I have a .wtv file, recorded from Windows Media Center, that I'd like to extract the subtitles from. The video is encrypted, but the subtitles are not (something I've been able to verify by using CCExtractor with it). FFMpeg lists the video as such:
Duration: 00:07:01.72, start: 2.214551, bitrate: 9154 kb/s
Stream #0:0[0xcc](eng): Audio: ac3, 48000 Hz, 5.1(side), fltp, 384 kb/s
Stream #0:1[0xcd]: Video: mpeg2video (Main), yuv420p(tv), 1920x1080 [SAR 1:1 DAR 16:9], Closed Captions, max. 25000 kb/s, 29.97 fps, 29.97 tbr, 10000k tbn, 59.94 tbc
Stream #0:2[0xce]: Subtitle: eia_608
When I try to run
ffmpeg -i myvideofile.wtv -an -vn -map 0:2 -c:s:0 srt test.srt
I see a lot of the following errors:
[wtv # 0x7fef79806e00] encrypted stream detected (st:1), decoding will likely fail
Last message repeated 8 times
[Closed caption Decoder # 0x7fef7982cc00] Data Ignored since exceeding screen width
and eventually:
Output file is empty, nothing was encoded (check -ss / -t / -frames parameters if used)
I don't mind it being able to decode the video stream, but is that causing the closed caption error? If it can't decode the video it doesn't know the screen width, perhaps? Is it possible to set the closed caption decoder to ignore such errors and output anyway (it's just in text format after all)?

Resources