Converting AAC stream to DASH MP4 with high fragment length precision - ffmpeg

For my HTML5 project I need to create a fragmented MP4 file with a single audio stream (no video), each fragment of which has a duration of exactly 0.1 second.
Accordingly to ffmpeg docs, you can accomplish that by passing a value in microseconds with '-frag_duration' - which I found to be working and playable with HTML5 MediaSource API:
$ ffmpeg -y -i input.aac -c:a libfdk_aac -b:a 64k -level:v 13 -r 25 -strict experimental -movflags empty_moov+default_base_moof -frag_duration 100000 output.mp4
As we have a 210 second audio split up by 0.1s fragments, I expect that in output.mp4 we'd have 2100 fragments, hence 2100 moof atoms. But, upon inspecting it I've figured out that we only have 1811 moof atoms - which means that some (or maybe even all) fragments are bigger than expected:
$ python ~/git/mp4viewer/src/showboxes.py output.mp4 |grep moof|wc -l
1811
Could anybody tell me what's wrong, and how could I accomplish what I want?
Right now my assumption is that during an encoding I have AAC frame length which is not a multiple of 0.1s, hence ffmpeg has no chance to produce the fragments that are strictly equal to 0.1s but I'm not sure. If somebody can confirm that - and let me know a way to explicitly set AAC frame_size in FFMPEG (I couldn't find anything like that in the docs), or completely disprove this - it would be also highly appreciated.

Related

ffmpeg - pts drift after seek with codec copy

There are two ffmpeg commands. First one is used to seek and copy video chunk. Second one is used to transcode video chunk applying select filter for exact frames match.
Here is how:
ffmpeg -ss <sec_from> -to <sec_to> -copyts -i <input> -map 0:v:0 -c copy chunk.mp4
ffmpeg -copyts -i chunk.mp4 -vf 'select=between(pts\,<pts_from>\,<pts_to>)' transcoded_cunk.mp4
It works fine most of the times. But for some inputs there is a little pts drift in downloaded chunk so missing frames is possible. In other words pts of the same packets (compared by hash) are shifted by several points (in my case 0,0002 sec) between input and chunked output.
What is the possible reason for such pts drift?
UPDATE 1: That's because ffmpeg set timescale=1000 in mvhd atom so edit list media time to start from looses precision. Is it possible to force mvhd timescale?
UPDATE 2: It's not possible to change mvhd timescale because ffmpeg uses constant (MOV_TIMESCALE 1000):
https://github.com/FFmpeg/FFmpeg/blob/82bd02a2c73bb5e6b7cf5e5eba486e279f1a7358/libavformat/movenc.c#L3498
UPDATE 3: same issue discussed earlier

ffmpeg output file size grows faster than linearly with movie length

I'm using ffmpeg to string together some .bmp images into a movie. In total, there are 1001 frames, amounting to 0:40 length. The command I'm using is
ffmpeg -f image2 -i render.%05d.bmp -c:v libx264 -s 512:268 render.mp4
The output file is 33,2 MB large, which is about twice the size of a full HD (about 16 times the pixels!) video of the same length. Apart from the file size being unreasonably large, I realized it grows faster than linearly (can't tell exactly if it is quadratic, exponential etc.) with the number of frames. After 100 frames it is about 1536 KB large (which is already too large), after 500 frames it is already 15104 KB, and after 1001 it finally arrives at 34085 KB.
My educated guess would be that for each frame it stores some information about all of the previous frames again, which makes absolutely no sense.
What am I doing wrong? Before you recommend libx265 to me: It turns the entire video green.
Use:
ffmpeg -i render.%05d.bmp -c:v libx264 -vf "scale=512:-2,format=yuv420p" -movflags +faststart output.mp4
If the output file size is too big add the -crf and -preset options as described in FFmpeg Wiki: H.264.
If the output is still too big change -c:v libx264 to -c:v libx265 but encoding will be slower. Your output was green when you tried x265 because of the pixel format: using format=yuv420p as shown in my example will fix that. See FFmpeg Wiki: H.265.
If you are targeting a specific output file size then use two-passes with -b:v (see either link above).

FFmpeg raw h.264 set pts value

I am currently using ffmpeg to convert a custom container media format to mp4. It is straightforward to dump all the h.264 frames to one file and the aac audio to another. Then I can combine the two and create an mp4 file with ffmpeg.
The problem is that the video source isn't always perfect. From time to time frames are dropped or late etc. This causes an A/V sync issue since the pts is generated using a constant rate by ffmpeg. The source format I am using has the PTS value but I cant figure out a way to pass it to ffmpeg with the raw h.264 frames.
I suppose it would be possible to create a demuxer for the custom format, but it seems like a lot effort. I looked into ffmpeg's .nut container format thinking that I might be able to convert from the custom container to .nut first. Unfortunately it seems more complex than it looks on the surface.
It seems like there should be an easy way to pass a frame and its PTS value to ffmpeg, but I haven't come across it yet. Any help would be appreciated.
Here is the ffmpeg command I am using
ffmpeg -f s16le -ac 1 -ar 48k -i source.audio -framerate 20 -i source.video -c:a aac -b:a 64k -r 20 -c:v h264_nvenc -rc:v vbr_hq -cq:v 19 -n out.mp4

ffmpeg convert one png to DNxHD

I'm trying to create a DNxHD file from one PNG file. The output should be "24000/1001" fps, 1920x1080, using the dnxhd codec. Every frame should be the same. The outputted stream must be 20 seconds in length.
I have a solution which uses filter_complex to loop the PNG for each frame, however this results in extremely large files. Given that I will be combining possibly multiple hundred DNxHD files into one AAF file, the output file size is too large.
Is there any improvement I can make on the command below which would achieve this file size reduction?
ffmpeg -i INFILE.png -y -nostdin -an -vcodec dnxhd -pix_fmt yuv422p -vb 36M -framerate 24000/1001 -filter_complex loop=479:1:0 OUFILE.dnxhd
I do not know ffmpeg all that well, this command has been constructed by copying parts of commands I have found online.
DNxHD is an intra-coded codec i.e. each frame is encoded (and thus decodable) independently of each other. So, the size can't be decreased without changing ratecontrol parameters like bitrate.
BTW, your command can be simplified to
ffmpeg -framerate 24000/1001 -loop 1 -t 20 -i IN.png -c:v dnxhd -pix_fmt yuv422p -b:v 36M OUT.dnxhd
framerate doesn't have any relevance when saving to a raw stream (.dnxhd); only when saving to containers like .mov. It is possible technically to construct a MOV file with only two frames, with the 2nd frame, having a timestamp 20 seconds forward, but not sure if your workflow will handle such files as desired.

ffmpeg setting for HD and normal quality

Hello i need to have two versions of the same file stored on my server, medium and HD quality, the thing is that don't really know ffmpeg that well so im just trying this is code at random, i'm using the code belo but I end up with a much larger file, however it works,it plays.
ffmpeg -i inputfile.wmv -vcodec libx264 -ar 44100 -b 200 -ab 56 -crf 22 -s 360x288 -vpre medium -f flv tmp.flv
Just need the two commands to create the 2 different files
You need to give more information about what bitrate, quality or target file size you are aiming for and the size and quality of your source material preferably including codecs used and relevant parameters.
You should read the manual or ffmpeg -h or both. There are several problems with your command line:
You are using constant rate factor, crf = 22, while still trying to limit the bitrate using -b 200.
Bitrate is specified in bits/s (unless you are using a very old ffmpeg), and 200 bps is not usable for anything, add k to get kilobits/s.
You have not specified an audio codec, but you have specified an audio bitrate, ffmpeg will try to guess the audio codec for you but I don't know what codec is the default for .flv-files.
I'm assuming that the command line you posted is supposed to be for the 'medium' quality file.
Some suggestions that you can try:
Try this first: specify audio codec, e.g. -acodec libmp3lame, or if the audio is already in a good format you can just copy it without modification using -acodec copy
Try a different rate factor, e.g. -crf 30, higher numbers mean uglier picture quality, but also smaller file size.
Try a different encoder preset, e.g. -vpre slow, in general, the slower presets enable features that require more CPU cycles when encoding but results in a better picture quality, see x264 --fullhelp or this page to see what each preset contains.
Do a 2-pass encode, link.
If you don't want to read all the documentation for ffmpeg and the codec parameters that you need I suggest you look at this cheat sheet, although the command line switches have changed over the different versions of ffmpeg so the examples might not work.
An example command line:
ffmpeg -i inputfile.wmv -vcodec libx264 -crf 25 -s 360x288 -vpre veryslow -acodec libmp3lame -ar 44100 -ab 56k -f flv tmp.flv
The parameter -s [size] is the size of the output video, in pixels, for the HD file you probably want something around 1280x720, if your material is 5:4 ratio (as 360x288 is) you'll want to try 1280x1024, 960x768 or 900x720. Don't set a size larger than the source material as that will simply upscale the video and you will (probably) end up with a larger file without any noticeable improvement in quality. The -ab parameter is the audio bitrate, you'll probably want to increase this parameter on the HD version as well.

Resources