How to fetch both live video frame and timestamp from ffmpeg to python on Windows

后端 未结 3 1615
一生所求
一生所求 2020-12-30 13:29

Searching for an alternative as OpenCV would not provide timestamps for live camera stream (on Windows), which are required in my computer vision a

相关标签:
3条回答
  • 2020-12-30 13:44

    Redirecting stderr works in python.
    So instead of this pipe = sp.Popen(command, stdout = sp.PIPE, stderr = sp.PIPE)
    do this pipe = sp.Popen(command, stdout = sp.PIPE, stderr = sp.STDOUT)

    We could avoid redirection by adding an asynchronous call to read both the standard streams (stdout and stderr) of ffmpeg. This would avoid any mixing of the video frame and timestamp and thus the error prone seperation. So modifying the original code to use threading module would look like this:

    # Python script to read video frames and timestamps using ffmpeg
    import subprocess as sp
    import threading
    
    import matplotlib.pyplot as plt
    import numpy
    import cv2
    
    ffmpeg_command = [ 'ffmpeg',
                       '-nostats', # do not print extra statistics
                        #'-debug_ts', # -debug_ts could provide timestamps avoiding showinfo filter (-vcodec copy). Need to check by providing expected fps TODO
                        '-r', '30', # output 30 frames per second
                        '-i', 'e:\sample.wmv',
                        '-an','-sn', #-an, -sn disables audio and sub-title processing respectively
                        '-pix_fmt', 'rgb24',
                        '-vcodec', 'rawvideo', 
                        #'-vcodec', 'copy', # very fast!, direct copy - Note: No Filters, No Decode/Encode, no quality loss
                        #'-vframes', '20', # process n video frames only. For Debugging
                        '-vf', 'showinfo', # showinfo videofilter provides frame timestamps as pts_time
                        '-f', 'image2pipe', 'pipe:1' ] # outputs to stdout pipe. can also use '-' which is redirected to pipe
    
    
    # seperate method to read images on stdout asynchronously
    def AppendProcStdout(proc, nbytes, AppendList):
        while proc.poll() is None: # continue while the process is alive
            AppendList.append(proc.stdout.read(nbytes)) # read image bytes at a time
    
    # seperate method to read image info. on stderr asynchronously
    def AppendProcStderr(proc, AppendList):
        while proc.poll() is None: # continue while the process is alive
            try: AppendList.append(proc.stderr.next()) # read stderr until empty
            except StopIteration: continue # ignore stderr empty exception and continue
    
    
    if __name__ == '__main__':
        # run ffmpeg command
        pipe = sp.Popen(ffmpeg_command, stdout=sp.PIPE, stderr=sp.PIPE) 
    
        # 2 threads to talk with ffmpeg stdout and stderr pipes
        framesList = [];
        frameDetailsList = []
        appendFramesThread = threading.Thread(group=None, target=AppendProcStdout, name='FramesThread', args=(pipe, 1280*720*3, framesList), kwargs=None, verbose=None) # assuming rgb video frame with size 1280*720 
        appendInfoThread = threading.Thread(group=None, target=AppendProcStderr, name='InfoThread', args=(pipe, frameDetailsList), kwargs=None, verbose=None) 
    
        # start threads to capture ffmpeg frames and info.
        appendFramesThread.start()
        appendInfoThread.start()
    
        # wait for few seconds and close - simulating cancel
        import time; time.sleep(2) 
        pipe.terminate() 
    
        # check if threads finished and close
        appendFramesThread.join() 
        appendInfoThread.join() 
    
        # save an image per 30 frames to disk 
        savedList = []
        for cnt,raw_image in enumerate(framesList):
            if (cnt%30 != 0): continue
            image1 =  numpy.fromstring(raw_image, dtype='uint8')
            image2 = image1.reshape((720,1280,3))  # assuming rgb image with size 1280 X 720
            # write video frame to file just to verify
            videoFrameName = 'video_frame{0}.png'.format(cnt)
            cv2.imwrite(videoFrameName,image2)
            savedList.append('{} {}'.format(videoFrameName, image2.shape))
    
        print '### Results ###'
        print 'Images captured: ({}) \nImages saved to disk:{}\n'.format(len(framesList), savedList) # framesList contains all the video frames got from the ffmpeg
        print 'Images info captured: \n', ''.join(frameDetailsList) # this contains all the timestamp details got from the ffmpeg showinfo videofilter and some initial noise text which can be easily removed while parsing
    
    0 讨论(0)
  • 2020-12-30 13:57

    You can use MoviePy:

    import moviepy.editor as mpy
    
    vid = mpy.VideoFileClip('e:\\sample.wmv')
    for timestamp, raw_img in vid.iter_frames(with_times=True):
        # do stuff
    
    0 讨论(0)
  • 2020-12-30 13:57

    You can try to specify the buffer size so you're sure the whole frame fits in it :

    bufsize = w*h*3 + 100 
    pipe = sp.Popen(command, bufsize=bufsize, stdout = sp.PIPE, stderr = sp.PIPE)
    

    with this set up, you can normally read on pipe.stdout for your frames and pipe.stderr for its info

    0 讨论(0)
提交回复
热议问题