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

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


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 ffmpegimport subprocess as spimport threadingimport matplotlib.pyplot as pltimport numpyimport cv2ffmpeg_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 asynchronouslydef 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 asynchronouslydef 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 continueif __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


You can use MoviePy:

import moviepy.editor as mpyvid = mpy.VideoFileClip('e:\\sample.wmv')for timestamp, raw_img in vid.iter_frames(with_times=True):    # do stuff


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