JuliaIO / VideoIO.jl

Reading and writing of video files in Julia via ffmpeg
https://juliaio.github.io/VideoIO.jl/stable
Other
125 stars 53 forks source link

Memory leak with open_video_out #359

Open yakir12 opened 2 years ago

yakir12 commented 2 years ago

When encoding a video with large frames, Julia crashes because the system runs out of memory. I made the following MWE to illustrate this (I stole @yiyuezhuo get_memory_usage function from this gist, thank you yiyuezhuo!)

using VideoIO, ColorTypes, FixedPointNumbers
function get_memory_usage()
    open("/proc/$(getpid())/statm") do io
      parse(Int, split(read(io, String))[1])
    end            
end
n = 20
y = zeros(Int, n)
sz = (3840, 2*2160)
frame = rand(RGB{N0f8}, sz)
encoder_options = (crf=23, preset="medium")
framerate = 300
open_video_out("video.mp4", frame, framerate=framerate, encoder_options=encoder_options) do writer
  for i in 1:n
    frame = rand(RGB{N0f8}, sz)
    write(writer, frame)
    y[i] = get_memory_usage()
  end
end
using UnicodePlots
lineplot(y)

which looks like this:

           ┌────────────────────────────────────────┐ 
   2100000 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠊⠉│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠤⠃⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡔⠁⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠔⠒⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠒⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠤⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠊⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⠀⢀⠤⠔⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠀⠀⡔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           │⠀⠀⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
   1700000 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
           └────────────────────────────────────────┘ 
           ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀20⠀ 
IanButterworth commented 2 years ago

What if you put a gc safepoint in the loop?

yakir12 commented 2 years ago

Sorry, I forgot to mention I already tried that. I added

Base.GC.gc()

to the loop but it didn't have any affect. Maybe "gc safepoint" refers to something else?

IanButterworth commented 2 years ago

I mean GC.safepoint() but what you did is the more explicit option to test the same thing.

Is the memory still retained after close and a sleep?

yakir12 commented 2 years ago

Not sure what exactly you mean by close and sleep, but if I have more than 20 frames in the example above then Julia crashes and the memory is released. If I am careful and use only 20 frames, then the memory isn't released before I exit that Julia session.

yakir12 commented 2 years ago

Not sure what exactly you mean by close and sleep, but if I have more than 20 frames in the example above then Julia crashes and the memory is released. If I am careful and use only 20 frames, then the memory isn't released before I exit that Julia session.

IanButterworth commented 2 years ago

I see the same and it's freed after close and GC

julia> begin
           n = 20
           y = zeros(Int, n)
           sz = (3840, 2*2160)
           frame = rand(RGB{N0f8}, sz)
           encoder_options = (crf=23, preset="medium")
           framerate = 300
           open_video_out("video.mp4", frame, framerate=framerate, encoder_options=encoder_options) do writer
               for i in 1:n
                   write(writer, frame)
                   @show Base.format_bytes(get_memory_usage())
               end
           end
           @show Base.format_bytes(get_memory_usage())
           GC.gc()
           @show Base.format_bytes(get_memory_usage())
           nothing
       end
Base.format_bytes(get_memory_usage()) = "4.256 MiB"
Base.format_bytes(get_memory_usage()) = "4.286 MiB"
Base.format_bytes(get_memory_usage()) = "4.293 MiB"
Base.format_bytes(get_memory_usage()) = "4.322 MiB"
Base.format_bytes(get_memory_usage()) = "4.352 MiB"
Base.format_bytes(get_memory_usage()) = "4.347 MiB"
Base.format_bytes(get_memory_usage()) = "4.377 MiB"
Base.format_bytes(get_memory_usage()) = "4.407 MiB"
Base.format_bytes(get_memory_usage()) = "4.402 MiB"
Base.format_bytes(get_memory_usage()) = "4.431 MiB"
Base.format_bytes(get_memory_usage()) = "4.461 MiB"
Base.format_bytes(get_memory_usage()) = "4.456 MiB"
Base.format_bytes(get_memory_usage()) = "4.486 MiB"
Base.format_bytes(get_memory_usage()) = "4.516 MiB"
Base.format_bytes(get_memory_usage()) = "4.511 MiB"
Base.format_bytes(get_memory_usage()) = "4.540 MiB"
Base.format_bytes(get_memory_usage()) = "4.570 MiB"
Base.format_bytes(get_memory_usage()) = "4.565 MiB"
Base.format_bytes(get_memory_usage()) = "4.595 MiB"
Base.format_bytes(get_memory_usage()) = "4.624 MiB"
Base.format_bytes(get_memory_usage()) = "4.630 MiB"
Base.format_bytes(get_memory_usage()) = "1.005 MiB"
yakir12 commented 2 years ago

So is this expected?

galenlynch commented 2 years ago

I can take a look at this

On Fri, May 20, 2022, at 07:56, Yakir Luc Gagnon wrote:

So is this expected?

— Reply to this email directly, view it on GitHub https://github.com/JuliaIO/VideoIO.jl/issues/359#issuecomment-1133006278, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABUDZT4CBXX4DTCTDQRKFZLVK6RYZANCNFSM5WPCI4RQ. You are receiving this because you are subscribed to this thread.Message ID: @.***>