-1

I'm using the H.264 library to compress a video frame by frame. It works, I can replay it back locally without any issue.

However, I need to send that video over the LAN and that LAN is rather busy already so I need to limit the size of each frame to a maximum of about 250Kb.

I use the following code to setup the parameters, but changing the bit rate values does not seem to have any effect on what the library does with the input frames:

x264_param_t param = {};
if(x264_param_default_preset(&param, "faster", nullptr) < 0)
{
    return -1;
}

param.i_csp = X264_CSP_I420;
param.i_width = 3840;
param.i_height = 2160;
param.i_keyint_max = static_cast<int>(f_frame_header.f_fps);
param.i_threads = X264_THREADS_AUTO;
param.b_vfr_input = 0;
param.b_repeat_headers = 1;
param.b_annexb = 1;

  // the following three parameters are the ones I tried to change with no results
  param.rc.i_bitrate = 100000;
  param.rc.i_vbv_max_bitrate = 100000;
  param.rc.i_vbv_buffer_size = 125000;

if(x264_param_apply_profile(&param, "high") < 0)
{
    return -1;
}

...enter loop reading frames and compressing them...

Changing the i_bitrate, i_vbv_max_bitrate and i_vbv_buffer_size parameters seems to have absolutely no effect on the size of the resulting frames. I still get some frames over 500Kb and in many even, rather large frames one after the other as the following sizes show:

20264
358875
218429
20728
25215
310230
36127
9077
29785
341541
222778
23542
21356
276772
25339
32459
421036
11179
6172
286070
193849

What I would need is the largest frame to be around 250,000 at its maximum. Now I understand that once in a while it go over a bit, but not 2×. That's just too much for my current available bandwidth.

What am I doing wrong in the parameters setup above?

I've seen this command line:

ffmpeg -i input -c:v libx264 -b:v 2M -maxrate 2M -bufsize 1M output.mp4

which would suggest that what I'm doing above should work (I tried all sorts of values including the ones one that command line). Yet the frame size does not really change between my runs.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • 2
    If your frame is 3840 x 2160 pixels x 3 bytes of color per pixel then it is 25 MB frame. That means you want to compress it about 100 times. That is high desire ... 40 times is considered rather great. Possible solutions are to reduce your frame rates, frame sizes, have higher throughput LAN and/or to research even more innovative compression methods than available. – Öö Tiib Jan 23 '23 at 01:54
  • 1
    `throw error();` is not C, the tag [tag:c] is improperly used. – 273K Jan 23 '23 at 02:03
  • 1
    @AlexisWilke Not sure how you are using `param = {};` to create an Object in C (or even in C++). Your code almost looks like some kind of ECMAscript-based language? Anyways the first comment is correct to say that you have too much data (picture size) to fit within 250kb of compression. You'll have to output at smaller resolution (_eg:_ 720p or smaller) for smoother playback or else user can wait longer for that larger 2K video frames (_eg:_ before playback begins, first buffer enough 2K frames to keep playback going smoothly while you get the remaining 2K frames). – VC.One Jan 23 '23 at 20:12
  • @VC.One There is really no javascript in my project. My project is C++ and I use that C library. So I think it's better to show this as C. As for the `x264_param_t`, it's a typedef so it works in C as well. The `var = {}` has been around for a good 10 years in both C and C++. Older compilers may have refused it if not including at least one element (`var = {0}`). See here: https://stackoverflow.com/questions/11152160/initializing-a-struct-to-0 – Alexis Wilke Jan 23 '23 at 20:44
  • All that said, I have to agree. The only way to make it work would be to either down-rez to 2K instead of 4K or make the images somewhat blurry, which then JPEG can compress better. Hmmm... – Alexis Wilke Jan 23 '23 at 21:30
  • @AlexisWilke Oh okay. I know about Structs. It wasn't obvious at quick glance that it was a C++ Struct. I haven't been serious with C++ since 2004, nowadays I just translate such code (usually audio/visual library functions) to the languages I currently use. _"The only way to make it work would be to either..."_ Yes **down-sizing** is your best option if you need to constrain the bitrate. Also try other H264 **Profiles** in your output settings (not 100% sure but I suspect, your H264 is currently at Baseline Profile?). The **Main** and **High** profiles might compress further. – VC.One Jan 25 '23 at 16:14
  • @AlexisWilke Consider wrapping the H264 frames (or better as groups of frames) into a data compression like GZip or BZ2 in case they reduce the size further when doing each LAN transmission. PS: Are you thinking to send each frame as a JPEG? Are they showing as a smaller file size? PPS: Also could try sending low-res frames but along with high-res keyframes. Then give movement to the keyframe by transferring motion vectors (copied from low and applied to high-res pixels). Motion will remain proportionally equal. Like this [Question's concept](https://stackoverflow.com/q/74125019/2057709). – VC.One Jan 25 '23 at 16:16
  • @VC.One I have an original 4:4:4 MP4 file. I extract all the frames as JPEG. Then I read those JPEG and build a movie compatible with the hardware I use. The output is H.264 (so MJPEG). Since the compression uses JPEG concepts, applying a blur makes the JPEG compress more. That's just the nature of the beast... – Alexis Wilke Jan 29 '23 at 15:24

1 Answers1

1

I tried with a blur applied to each frame to see whether it work help. Yes! It did. The result is a movie which is 2.44 times smaller than the original.

To load each JPEG image from the original, I use ImageMagick++ (in C++), so I just do the following blur on each image:

image.blur(0.0, 5.0);

and that took about 10 hours total (without the blur the same processing took about 40 minutes) but it was worth it since in the end the compressed movie went from 1,293,272,023 bytes to only 529,556,265 bytes (2.44218 times smaller). The blur added about 3.3 seconds of processing per frame and there are a little over 11,000 frames in the original.

Note: I used 5.0 for the blur because I have 4K images and although I can see a sharp difference when I look at one frame, when playing back the resulting movie, I don't notice the final blur. If you have smaller images, you probably want to use a smaller number. It looks like many people use a blur of just 0.05 and already have good results in compression ratios.

In C, use the BlurImage() function:

Image *BlurImage(const Image *image,const double radius,
                 const double sigma,ExceptionInfo *exception)

Here are some references about using a blur to further compress JPEG images as it helps eliminates sharp edges which do not compress well in the JPEG format (as sharp edge are not as natural):

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156