Videocapture set framenumber gives a None frame (not always)

I’m using a simple python code to extract thumbnail from a video.
Using opencv headless version 4.6.0.66 as AWS lambda layer
AWS Lambda has 10240MB memory and 1024MB Ephemeral storage.
Runtime is Python 3.9
Architecture is x86_64

The video capture set is giving Frame as None sometimes. Not able to narrow it down to a specific setting / case as larger files are giving resutls and smaller files choke some times.

cam = cv2.VideoCapture(input_file_signed_url)
if not cam.isOpened():
    raise Exception("Error: Could not open video.")
cam.set(cv2.CAP_PROP_POS_FRAMES, thumbnail_frame_number)
_, frame = cam.read()
cv2.imwrite(tmp_thumbnail_filename, frame)

What could go wrong here?

can you provide video files for which this happened?

is the video even long enough to have the frame index you request?

run ffprobe (ffmpeg project) on the failing video files. what is the report?

videos only secondarily can be indexed by frame number. primarily they are indexed by timestamp.

try CAP_PROP_POS_MSEC. I’d expect that to do better.

Thanks for your reply crackwitz. Yes the Video is long enough.

Unfortunately I can’t share the file. I can share the ffprobe output.

Also, like you suggested I have changed my approach to read frame with millisecs if not available with frame number, but no luck. When I set the millisecs its setting it to wrong millisecs rather than the one I set to

time1 = cam.get(cv2.CAP_PROP_POS_MSEC)
fps = cam.get(cv2.CAP_PROP_FPS)
total_frames = cam.get(cv2.CAP_PROP_FRAME_COUNT)
print(time1, fps, total_frames)
ms_per_frame = 1000 / fps
milliseconds = thumbnail_frame_number * ms_per_frame
cam.set(cv2.CAP_PROP_POS_FRAMES, thumbnail_frame_number)
_, frame = cam.read()
if frame is None:
     print(f"Warning: Could not read frame. Setting milliseconds to {milliseconds}")
     cam.set(cv2.CAP_PROP_POS_MSEC, milliseconds)
time2 = cam.get(cv2.CAP_PROP_POS_MSEC)
ressult, frame = cam.read()    

Output is
0.0 25.0 138769.0
Warning: Could not read frame. Setting milliseconds to 122040.0
4881600.0 — wrong millis is set

ffprobe version 7.1.1 Copyright (c) 2007-2025 the FFmpeg developers
  built with Apple clang version 16.0.0 (clang-1600.0.26.6)
  configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/7.1.1_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags='-Wl,-ld_classic' --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libharfbuzz --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-audiotoolbox --enable-neon
  libavutil      59. 39.100 / 59. 39.100
  libavcodec     61. 19.101 / 61. 19.101
  libavformat    61.  7.100 / 61.  7.100
  libavdevice    61.  3.100 / 61.  3.100
  libavfilter    10.  4.100 / 10.  4.100
  libswscale      8.  3.100 /  8.  3.100
  libswresample   5.  3.100 /  5.  3.100
  libpostproc    58.  3.100 / 58.  3.100
Input #0, mxf, from 'opencv-test-file.mxf':
  Metadata:
    operational_pattern_ul: 060e2b34.04010101.0d010201.01010900
    uid             : 9c5eaf61-766b-11e9-83be-90e2ba8ac885
    generation_uid  : 9c5eaf62-766b-11e9-bd75-90e2ba8ac885
    company_name    : Telestream
    product_name    : Flip Technology
    product_version_num: 1.0.0.0.0
    product_version : 3.0
    application_platform: win32
    product_uid     : ffeeddcc-bbaa-9988-7766-554433221100
    toolkit_version_num: 4.5.4.0.3
    modification_date: 2019-05-14T17:13:36.824000Z
    material_package_umid: 0x060A2B340101010501010D1213000000B74FB20317860580B6DD90E2BA8AC885
    timecode        : 09:59:40:00
  Duration: 01:32:30.76, start: 0.000000, bitrate: 55081 kb/s
  Stream #0:0: Video: mpeg2video (4:2:2), yuv422p(tv, bt709, top first), 1920x1080 [SAR 1:1 DAR 16:9], 50000 kb/s, 25 fps, 25 tbr, 25 tbn
      Metadata:
        file_package_umid: 0x060A2B340101010501010D121363BB74864FB20317860580C65590E2BA8AC885
        file_package_name: Source Package
        track_name      : Track 1
      Side data:
        cpb: bitrate max/min/avg: 50000000/0/0 buffer size: 47185920 vbv_delay: N/A
  Stream #0:1: Audio: pcm_s24le, 48000 Hz, 1 channels, s32 (24 bit), 1152 kb/s
      Metadata:
        file_package_umid: 0x060A2B340101010501010D121363BB74864FB20317860580C65590E2BA8AC885
        file_package_name: Source Package
        track_name      : Track 2
  Stream #0:2: Audio: pcm_s24le, 48000 Hz, 1 channels, s32 (24 bit), 1152 kb/s
      Metadata:
        file_package_umid: 0x060A2B340101010501010D121363BB74864FB20317860580C65590E2BA8AC885
        file_package_name: Source Package
        track_name      : Track 3
  Stream #0:3: Audio: pcm_s24le, 48000 Hz, 1 channels, s32 (24 bit), 1152 kb/s
      Metadata:
        file_package_umid: 0x060A2B340101010501010D121363BB74864FB20317860580C65590E2BA8AC885
        file_package_name: Source Package
        track_name      : Track 4
  Stream #0:4: Audio: pcm_s24le, 48000 Hz, 1 channels, s32 (24 bit), 1152 kb/s
      Metadata:
        file_package_umid: 0x060A2B340101010501010D121363BB74864FB20317860580C65590E2BA8AC885
        file_package_name: Source Package
        track_name      : Track 5

If this is a bug we’ll need a video file which reproduces it to track down the cause. I assume that everything works if you manually decode all the frames?

If you can’t share the video then maybe you can find out what the difference is between the actual frame returned and the frame you request. i.e. If you request frame 10,20,30,40 etc. which frame do you actually get when seeking? If it works for low numbered frames where does he process brake down?

The lower number frames doesn’t work as well. The Frame returned is None.

Does that mean it works when you don’t seek?
Which frame does seeking to frame 0,1,2,..,10,20 actually return?

when in doubt, use PyAV.

there is still this

@crackwitz - not able to set mills in cam.set(cv2.CAP_PROP_POS_MSEC, milliseconds) is a known bug? Thanks.