Best Python code snippet using playwright-python
rippy.py
Source:rippy.py  
1#!/usr/bin/env python2"""Rippy!3This script helps to convert video from one format to another.4This is useful for ripping DVD to mpeg4 video (XviD, DivX).5Features:6    * automatic crop detection7    * mp3 audio compression with resampling options8    * automatic bitrate calculation based on desired target size9    * optional interlace removal, b/w video optimization, video scaling10Run the script with no arguments to start with interactive prompts:11    rippy.py12Run the script with the filename of a config to start automatic mode:13    rippy.py rippy.conf14After Rippy is finished it saves the current configuation in a file called15'rippy.conf' in the local directoy. This can be used to rerun process using the16exact same settings by passing the filename of the conf file as an argument to17Rippy. Rippy will read the options from the file instead of asking you for18options interactively. So if you run rippy with 'dry_run=1' then you can run19the process again later using the 'rippy.conf' file. Don't forget to edit20'rippy.conf' to set 'dry_run=0'!21If you run rippy with 'dry_run' and 'verbose' true then the output generated is22valid command line commands. you could (in theory) cut-and-paste the commands23to a shell prompt. You will need to tweak some values such as crop area and bit24rate because these cannot be calculated in a dry run. This is useful if you25want to get an idea of what Rippy plans to do.26For all the trouble that Rippy goes through to calculate the best bitrate for a27desired target video size it sometimes fails to get it right. Sometimes the28final video size will differ more than you wanted from the desired size, but if29you are really motivated and have a lot of time on your hands then you can run30Rippy again with a manually calculated bitrate. After all compression is done31the first time Rippy will recalculate the bitrate to give you the nearly exact32bitrate that would have worked. You can then edit the 'rippy.conf' file; set33the video_bitrate with this revised bitrate; and then run Rippy all over again.34There is nothing like 4-pass video compression to get it right! Actually, this35could be done in three passes since I don't need to do the second pass36compression before I calculate the revised bitrate. I'm also considering an37enhancement where Rippy would compress ten spread out chunks, 1-minute in38length to estimate the bitrate.39Free, open source, and all that good stuff.40Rippy Copyright (c) 2006 Noah Spurrier41Permission is hereby granted, free of charge, to any person obtaining a copy42of this software and associated documentation files (the "Software"), to deal43in the Software without restriction, including without limitation the rights44to use, copy, modify, merge, publish, distribute, sublicense, and/or sell45copies of the Software, and to permit persons to whom the Software is46furnished to do so, subject to the following conditions:47The above copyright notice and this permission notice shall be included in all48copies or substantial portions of the Software.49THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,50EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF51MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.52IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,53DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR54OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE55USE OR OTHER DEALINGS IN THE SOFTWARE.56Noah Spurrier57$Id: rippy.py 498 2007-12-17 13:44:19Z noah $58"""59import sys, os, re, math, stat, getopt, traceback, types, time60import pexpect61__version__ = '1.2'62__revision__ = '$Revision: 11 $'63__all__ = ['main', __version__, __revision__]64GLOBAL_LOGFILE_NAME = "rippy_%d.log" % os.getpid()65GLOBAL_LOGFILE = open (GLOBAL_LOGFILE_NAME, "wb")66###############################################################################67# This giant section defines the prompts and defaults used in interactive mode.68###############################################################################69# Python dictionaries are unordered, so70# I have this list that maintains the order of the keys.71prompts_key_order = (72'verbose_flag',73'dry_run_flag',74'video_source_filename',75'video_chapter',76'video_final_filename',77'video_length',78'video_aspect_ratio',79'video_scale',80'video_encode_passes',81'video_codec',82'video_fourcc_override',83'video_bitrate',84'video_bitrate_overhead',85'video_target_size',86'video_crop_area',87'video_deinterlace_flag',88'video_gray_flag',89'subtitle_id',90'audio_id',91'audio_codec',92'audio_raw_filename',93'audio_volume_boost',94'audio_sample_rate',95'audio_bitrate',96#'audio_lowpass_filter',97'delete_tmp_files_flag'98)99#100# The 'prompts' dictionary holds all the messages shown to the user in101# interactive mode. The 'prompts' dictionary schema is defined as follows:102#    prompt_key : ( default value, prompt string, help string, level of difficulty (0,1,2) )103#104prompts = {105'video_source_filename':("dvd://1", 'video source filename?', """This is the filename of the video that you want to convert from.106It can be any file that mencoder supports.107You can also choose a DVD device using the dvd://1 syntax.108Title 1 is usually the main title on a DVD.""",0),109'video_chapter':("none",'video chapter?',"""This is the chapter number. Usually disks such as TV series seasons will be divided into chapters. Maybe be set to none.""",0),110'video_final_filename':("video_final.avi", "video final filename?", """This is the name of the final video.""",0),111'audio_raw_filename':("audiodump.wav", "audio raw filename?", """This is the audio raw PCM filename. This is prior to compression.112Note that mplayer automatically names this audiodump.wav, so don't change this.""",1000),113#'audio_compressed_filename':("audiodump.mp3","Audio compressed filename?", """This is the name of the compressed audio that will be mixed114#into the final video. Normally you don't need to change this.""",2),115'video_length':("none","video length in seconds?","""This sets the length of the video in seconds. This is used to estimate the116bitrate for a target video file size. Set to 'calc' to have Rippy calculate117the length. Set to 'none' if you don't want rippy to estimate the bitrate --118you will have to manually specify bitrate.""",1),119'video_aspect_ratio':("calc","aspect ratio?","""This sets the aspect ratio of the video. Most DVDs are 16/9 or 4/3.""",1),120'video_scale':("none","video scale?","""This scales the video to the given output size. The default is to do no scaling.121You may type in a resolution such as 320x240 or you may use presets.122    qntsc: 352x240 (NTSC quarter screen)123    qpal:  352x288 (PAL quarter screen)124    ntsc:  720x480 (standard NTSC)125    pal:   720x576 (standard PAL)126    sntsc: 640x480 (square pixel NTSC)127    spal:  768x576 (square pixel PAL)""",1),128'video_codec':("mpeg4","video codec?","""This is the video compression to use. This is passed directly to mencoder, so129any format that it recognizes should work. For XviD or DivX use mpeg4.130Almost all MS Windows systems support wmv2 out of the box.131Some common codecs include:132mjpeg, h263, h263p, h264, mpeg4, msmpeg4, wmv1, wmv2, mpeg1video, mpeg2video, huffyuv, ffv1.133""",2),134'audio_codec':("mp3","audio codec?","""This is the audio compression to use. This is passed directly to mencoder, so135any format that it recognizes will work.136Some common codecs include:137mp3, mp2, aac, pcm138See mencoder manual for details.""",2),139'video_fourcc_override':("XVID","force fourcc code?","""This forces the fourcc codec to the given value. XVID is safest for Windows.140The following are common fourcc values:141    FMP4 - This is the mencoder default. This is the "real" value.142    XVID - used by Xvid (safest)143    DX50 -144    MP4S - Microsoft""",2),145'video_encode_passes':("1","number of encode passes?","""This sets how many passes to use to encode the video. You can choose 1 or 2.146Using two pases takes twice as long as one pass, but produces a better147quality video. I found that the improvement is not that impressive.""",1),148'verbose_flag':("Y","verbose output?","""This sets verbose output. If true then all commands and arguments are printed149before they are run. This is useful to see exactly how commands are run.""",1),150'dry_run_flag':("N","dry run?","""This sets 'dry run' mode. If true then commands are not run. This is useful151if you want to see what would the script would do.""",1),152'video_bitrate':("calc","video bitrate?","""This sets the video bitrate. This overrides video_target_size.153Set to 'calc' to automatically estimate the bitrate based on the154video final target size. If you set video_length to 'none' then155you will have to specify this video_bitrate.""",1),156'video_target_size':("737280000","video final target size?","""This sets the target video size that you want to end up with.157This is over-ridden by video_bitrate. In other words, if you specify158video_bitrate then video_target_size is ignored.159Due to the unpredictable nature of VBR compression the final video size160may not exactly match. The following are common CDR sizes:161    180MB CDR (21 minutes) holds 193536000 bytes162    550MB CDR (63 minutes) holds 580608000 bytes163    650MB CDR (74 minutes) holds 681984000 bytes164    700MB CDR (80 minutes) holds 737280000 bytes""",0),165'video_bitrate_overhead':("1.0","bitrate overhead factor?","""Adjust this value if you want to leave more room for166other files such as subtitle files.167If you specify video_bitrate then this value is ignored.""",2),168'video_crop_area':("detect","crop area?","""This sets the crop area to remove black bars from the top or sides of the video.169This helps save space. Set to 'detect' to automatically detect the crop area.170Set to 'none' to not crop the video. Normally you don't need to change this.""",1),171'video_deinterlace_flag':("N","is the video interlaced?","""This sets the deinterlace flag. If set then mencoder will be instructed172to filter out interlace artifacts (using '-vf pp=md').""",1),173'video_gray_flag':("N","is the video black and white (gray)?","""This improves output for black and white video.""",1),174'subtitle_id':("None","Subtitle ID stream?","""This selects the subtitle stream to extract from the source video.175Normally, 0 is the English subtitle stream for a DVD.176Subtitles IDs with higher numbers may be other languages.""",1),177'audio_id':("128","audio ID stream?","""This selects the audio stream to extract from the source video.178If your source is a VOB file (DVD) then stream IDs start at 128.179Normally, 128 is the main audio track for a DVD.180Tracks with higher numbers may be other language dubs or audio commentary.""",1),181'audio_sample_rate':("32000","audio sample rate (Hz) 48000, 44100, 32000, 24000, 12000","""This sets the rate at which the compressed audio will be resampled.182DVD audio is 48 kHz whereas music CDs use 44.1 kHz. The higher the sample rate183the more space the audio track will take. That will leave less space for video.18432 kHz is a good trade-off if you are trying to fit a video onto a CD.""",1),185'audio_bitrate':("96","audio bitrate (kbit/s) 192, 128, 96, 64?","""This sets the bitrate for MP3 audio compression.186The higher the bitrate the more space the audio track will take.187That will leave less space for video. Most people find music to be acceptable188at 128 kBitS. 96 kBitS is a good trade-off if you are trying to fit a video onto a CD.""",1),189'audio_volume_boost':("none","volume dB boost?","""Many DVDs have very low audio volume. This sets an audio volume boost in Decibels.190Values of 6 to 10 usually adjust quiet DVDs to a comfortable level.""",1),191#'audio_lowpass_filter':("16","audio lowpass filter (kHz)?","""This sets the low-pass filter for the audio.192#Normally this should be half of the audio sample rate.193#This improves audio compression and quality.194#Normally you don't need to change this.""",1),195'delete_tmp_files_flag':("N","delete temporary files when finished?","""If Y then %s, audio_raw_filename, and 'divx2pass.log' will be deleted at the end."""%GLOBAL_LOGFILE_NAME,1)196}197##############################################################################198# This is the important convert control function199##############################################################################200def convert (options):201    """This is the heart of it all -- this performs an end-to-end conversion of202    a video from one format to another. It requires a dictionary of options.203    The conversion process will also add some keys to the dictionary204    such as length of the video and crop area. The dictionary is returned.205    This options dictionary could be used again to repeat the convert process206    (it is also saved to rippy.conf as text).207    """208    if options['subtitle_id'] is not None:209        print "# extract subtitles"210        apply_smart (extract_subtitles, options)211    else:212        print "# do not extract subtitles."213    # Optimization214    # I really only need to calculate the exact video length if the user215    # selected 'calc' for video_bitrate216    # or217    # selected 'detect' for video_crop_area.218    if options['video_bitrate']=='calc' or options['video_crop_area']=='detect':219        # As strange as it seems, the only reliable way to calculate the length220        # of a video (in seconds) is to extract the raw, uncompressed PCM audio stream221        # and then calculate the length of that. This is because MP4 video is VBR, so222        # you cannot get exact time based on compressed size.223        if options['video_length']=='calc':224            print "# extract PCM raw audio to %s" % (options['audio_raw_filename'])225            apply_smart (extract_audio, options)226            options['video_length'] = apply_smart (get_length, options)227            print "# Length of raw audio file : %d seconds (%0.2f minutes)" % (options['video_length'], float(options['video_length'])/60.0)228        if options['video_bitrate']=='calc':229            options['video_bitrate'] = options['video_bitrate_overhead'] * apply_smart (calc_video_bitrate, options) 230        print "# video bitrate : " + str(options['video_bitrate'])231        if options['video_crop_area']=='detect':232            options['video_crop_area'] = apply_smart (crop_detect, options)233        print "# crop area : " + str(options['video_crop_area'])234        print "# compression estimate"235        print apply_smart (compression_estimate, options)236    print "# compress video"237    apply_smart (compress_video, options)238    'audio_volume_boost',239    print "# delete temporary files:",240    if options['delete_tmp_files_flag']:241        print "yes"242        apply_smart (delete_tmp_files, options)243    else:244        print "no"245    # Finish by saving options to rippy.conf and 246    # calclating if final_size is less than target_size.247    o = ["# options used to create video\n"]248    video_actual_size = get_filesize (options['video_final_filename'])249    if options['video_target_size'] != 'none':250        revised_bitrate = calculate_revised_bitrate (options['video_bitrate'], options['video_target_size'], video_actual_size)251        o.append("# revised video_bitrate : %d\n" % revised_bitrate)252    for k,v in options.iteritems():253        o.append (" %30s : %s\n" % (k, v))254    print '# '.join(o)255    fout = open("rippy.conf","wb").write(''.join(o))256    print "# final actual video size = %d" % video_actual_size257    if options['video_target_size'] != 'none':258        if video_actual_size > options['video_target_size']:259            print "# FINAL VIDEO SIZE IS GREATER THAN DESIRED TARGET"260            print "# final video size is %d bytes over target size" % (video_actual_size - options['video_target_size'])261        else:262            print "# final video size is %d bytes under target size" % (options['video_target_size'] - video_actual_size)263        print "# If you want to run the entire compression process all over again"264        print "# to get closer to the target video size then trying using a revised"265        print "# video_bitrate of %d" % revised_bitrate266    return options267##############################################################################268def exit_with_usage(exit_code=1):269    print globals()['__doc__']270    print 'version:', globals()['__version__']271    sys.stdout.flush()272    os._exit(exit_code)273def check_missing_requirements ():274    """This list of missing requirements (mencoder, mplayer, lame, and mkvmerge).275    Returns None if all requirements are in the execution path.276    """277    missing = []278    if pexpect.which("mencoder") is None:279        missing.append("mencoder")280    if pexpect.which("mplayer") is None:281        missing.append("mplayer")282    #if pexpect.which("lame") is None:283    #    missing.append("lame")284    #if pexpect.which("mkvmerge") is None:285    #    missing.append("mkvmerge")286    if len(missing)==0:287        return None288    return missing289def input_option (message, default_value="", help=None, level=0, max_level=0):290    """This is a fancy raw_input function.291    If the user enters '?' then the contents of help is printed.292    293    The 'level' and 'max_level' are used to adjust which advanced options294    are printed. 'max_level' is the level of options that the user wants295    to see. 'level' is the level of difficulty for this particular option.296    If this level is <= the max_level the user wants then the297    message is printed and user input is allowed; otherwise, the298    default value is returned automatically without user input.299    """300    if default_value != '':301        message = "%s [%s] " % (message, default_value)302    if level > max_level:303        return default_value304    while 1:305        user_input = raw_input (message)306        if user_input=='?':307            print help308        elif user_input=='':309            return default_value310        else:311            break312    return user_input313def progress_callback (d=None):314    """This callback simply prints a dot to show activity.315    This is used when running external commands with pexpect.run.316    """317    sys.stdout.write (".")318    sys.stdout.flush()319def run(cmd):320    global GLOBAL_LOGFILE321    print >>GLOBAL_LOGFILE, cmd322    (command_output, exitstatus) = pexpect.run(cmd, events={pexpect.TIMEOUT:progress_callback}, timeout=5, withexitstatus=True, logfile=GLOBAL_LOGFILE)323    if exitstatus != 0:324        print "RUN FAILED. RETURNED EXIT STATUS:", exitstatus325        print >>GLOBAL_LOGFILE, "RUN FAILED. RETURNED EXIT STATUS:", exitstatus326    return (command_output, exitstatus)327def apply_smart (func, args):328    """This is similar to func(**args), but this won't complain about 329    extra keys in 'args'. This ignores keys in 'args' that are 330    not required by 'func'. This passes None to arguments that are331    not defined in 'args'. That's fine for arguments with a default valeue, but332    that's a bug for required arguments. I should probably raise a TypeError.333    The func parameter can be a function reference or a string.334    If it is a string then it is converted to a function reference.335    """336    if type(func) is type(''):337        if func in globals():338            func = globals()[func]339        else:340            raise NameError("name '%s' is not defined" % func)341    if hasattr(func,'im_func'): # Handle case when func is a class method.342        func = func.im_func343    argcount = func.func_code.co_argcount344    required_args = dict([(k,args.get(k)) for k in func.func_code.co_varnames[:argcount]])345    return func(**required_args)346def count_unique (items):347    """This takes a list and returns a sorted list of tuples with a count of each unique item in the list.348    Example 1:349        count_unique(['a','b','c','a','c','c','a','c','c'])350    returns:351        [(5,'c'), (3,'a'), (1,'b')]352    Example 2 -- get the most frequent item in a list:353        count_unique(['a','b','c','a','c','c','a','c','c'])[0][1]354    returns:355        'c'356    """357    stats = {}358    for i in items:359        if i in stats:360            stats[i] = stats[i] + 1361        else:362            stats[i] = 1363    stats = [(v, k) for k, v in stats.items()]364    stats.sort()365    stats.reverse()366    return stats367def calculate_revised_bitrate (video_bitrate, video_target_size, video_actual_size):368    """This calculates a revised video bitrate given the video_bitrate used,369    the actual size that resulted, and the video_target_size.370    This can be used if you want to compress the video all over again in an371    attempt to get closer to the video_target_size.372    """373    return int(math.floor(video_bitrate * (float(video_target_size) / float(video_actual_size))))374def get_aspect_ratio (video_source_filename):375    """This returns the aspect ratio of the original video.376    This is usualy 1.78:1(16/9) or 1.33:1(4/3).377    This function is very lenient. It basically guesses 16/9 whenever378    it cannot figure out the aspect ratio.379    """380    cmd = "mplayer '%s' -vo png -ao null -frames 1" % video_source_filename381    (command_output, exitstatus) = run(cmd)382    ar = re.findall("Movie-Aspect is ([0-9]+\.?[0-9]*:[0-9]+\.?[0-9]*)", command_output)383    if len(ar)==0:384        return '16/9'385    if ar[0] == '1.78:1':386        return '16/9'387    if ar[0] == '1.33:1':388        return '4/3'389    return '16/9'390    #idh = re.findall("ID_VIDEO_HEIGHT=([0-9]+)", command_output)391    #if len(idw)==0 or len(idh)==0:392    #    print 'WARNING!'393    #    print 'Could not get aspect ration. Assuming 1.78:1 (16/9).'394    #    return 1.78395    #return float(idw[0])/float(idh[0])396#ID_VIDEO_WIDTH=720397#ID_VIDEO_HEIGHT=480398#Movie-Aspect is 1.78:1 - prescaling to correct movie aspect.399def get_aid_list (video_source_filename):400    """This returns a list of audio ids in the source video file.401    TODO: Also extract ID_AID_nnn_LANG to associate language. Not all DVDs include this.402    """403    cmd = "mplayer '%s' -vo null -ao null -frames 0 -identify" % video_source_filename404    (command_output, exitstatus) = run(cmd)405    idl = re.findall("ID_AUDIO_ID=([0-9]+)", command_output)406    idl.sort()407    return idl408def get_sid_list (video_source_filename):409    """This returns a list of subtitle ids in the source video file.410    TODO: Also extract ID_SID_nnn_LANG to associate language. Not all DVDs include this.411    """412    cmd = "mplayer '%s' -vo null -ao null -frames 0 -identify" % video_source_filename413    (command_output, exitstatus) = run(cmd)414    idl = re.findall("ID_SUBTITLE_ID=([0-9]+)", command_output)415    idl.sort()416    return idl417    418def extract_audio (video_source_filename, audio_id=128, verbose_flag=0, dry_run_flag=0):419    """This extracts the given audio_id track as raw uncompressed PCM from the given source video.420        Note that mplayer always saves this to audiodump.wav.421        At this time there is no way to set the output audio name.422    """423    #cmd = "mplayer %(video_source_filename)s -vc null -vo null -aid %(audio_id)s -ao pcm:fast -noframedrop" % locals()424    cmd = "mplayer -quiet '%(video_source_filename)s' -vc dummy -vo null -aid %(audio_id)s -ao pcm:fast -noframedrop" % locals()425    if verbose_flag: print cmd426    if not dry_run_flag:427        run(cmd)428        print429def extract_subtitles (video_source_filename, subtitle_id=0, verbose_flag=0, dry_run_flag=0):430    """This extracts the given subtitle_id track as VOBSUB format from the given source video.431    """432    cmd = "mencoder -quiet '%(video_source_filename)s' -o /dev/null -nosound -ovc copy -vobsubout subtitles -vobsuboutindex 0 -sid %(subtitle_id)s" % locals()433    if verbose_flag: print cmd434    if not dry_run_flag:435        run(cmd)436        print437def get_length (audio_raw_filename):438    """This attempts to get the length of the media file (length is time in seconds).439    This should not be confused with size (in bytes) of the file data.440    This is best used on a raw PCM AUDIO file because mplayer cannot get an accurate441    time for many compressed video and audio formats -- notably MPEG4 and MP3.442    Weird...443    This returns -1 if it cannot get the length of the given file.444    """445    cmd = "mplayer %s -vo null -ao null -frames 0 -identify" % audio_raw_filename446    (command_output, exitstatus) = run(cmd)447    idl = re.findall("ID_LENGTH=([0-9.]*)", command_output)448    idl.sort()449    if len(idl) != 1:450        print "ERROR: cannot get length of raw audio file."451        print "command_output of mplayer identify:"452        print command_output453        print "parsed command_output:"454        print str(idl)455        return -1456    return float(idl[0])457def get_filesize (filename):458    """This returns the number of bytes a file takes on storage."""459    return os.stat(filename)[stat.ST_SIZE]460def calc_video_bitrate (video_target_size, audio_bitrate, video_length, extra_space=0, dry_run_flag=0):461    """This gives an estimate of the video bitrate necessary to462    fit the final target size.  This will take into account room to463    fit the audio and extra space if given (for container overhead or whatnot).464        video_target_size is in bytes,465        audio_bitrate is bits per second (96, 128, 256, etc.) ASSUMING CBR,466        video_length is in seconds,467        extra_space is in bytes.468    a 180MB CDR (21 minutes) holds 193536000 bytes.469    a 550MB CDR (63 minutes) holds 580608000 bytes.470    a 650MB CDR (74 minutes) holds 681984000 bytes.471    a 700MB CDR (80 minutes) holds 737280000 bytes.472    """473    if dry_run_flag:474        return -1475    if extra_space is None: extra_space = 0476    #audio_size = os.stat(audio_compressed_filename)[stat.ST_SIZE]477    audio_size = (audio_bitrate * video_length * 1000) / 8.0478    video_target_size = video_target_size - audio_size - extra_space479    return (int)(calc_video_kbitrate (video_target_size, video_length))480def calc_video_kbitrate (target_size, length_secs):481    """Given a target byte size free for video data, this returns the bitrate in kBit/S.482    For mencoder vbitrate 1 kBit = 1000 Bits -- not 1024 bits.483        target_size = bitrate * 1000 * length_secs / 8484        target_size = bitrate * 125 * length_secs485        bitrate     = target_size/(125*length_secs)486    """487    return int(target_size / (125.0 * length_secs))488def crop_detect (video_source_filename, video_length, dry_run_flag=0):489    """This attempts to figure out the best crop for the given video file.490    Basically it runs crop detect for 10 seconds on five different places in the video.491    It picks the crop area that was most often detected.492    """493    skip = int(video_length/9) # offset to skip (-ss option in mencoder)494    sample_length = 10495    cmd1 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename,   skip, sample_length)496    cmd2 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 2*skip, sample_length)497    cmd3 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 4*skip, sample_length)498    cmd4 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 6*skip, sample_length)499    cmd5 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 8*skip, sample_length)500    if dry_run_flag:501        return "0:0:0:0"502    (command_output1, exitstatus1) = run(cmd1)503    (command_output2, exitstatus2) = run(cmd2)504    (command_output3, exitstatus3) = run(cmd3)505    (command_output4, exitstatus4) = run(cmd4)506    (command_output5, exitstatus5) = run(cmd5)507    idl = re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output1)508    idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output2)509    idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output3)510    idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output4)511    idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output5)512    items_count = count_unique(idl)513    return items_count[0][1]514def build_compression_command (video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None, seek_skip=None, seek_length=None, video_chapter=None):515#Notes:For DVD, VCD, and SVCD use acodec=mp2 and vcodec=mpeg2video:516#mencoder movie.avi -o movie.VOB -ovc lavc -oac lavc -lavcopts acodec=mp2:abitrate=224:vcodec=mpeg2video:vbitrate=2000517    #518    # build video filter (-vf) argument 519    #520    video_filter = ''521    if video_crop_area and video_crop_area.lower()!='none':522        video_filter = video_filter + 'crop=%s' % video_crop_area523    if video_deinterlace_flag:524        if video_filter != '':525            video_filter = video_filter + ','526        video_filter = video_filter + 'pp=md'527    if video_scale and video_scale.lower()!='none':528        if video_filter != '':529            video_filter = video_filter + ','530        video_filter = video_filter + 'scale=%s' % video_scale531    # optional video rotation -- were you holding your camera sideways?532    #if video_filter != '':533    #    video_filter = video_filter + ','534    #video_filter = video_filter + 'rotate=2' 535    if video_filter != '':536        video_filter = '-vf ' + video_filter537    #538    # build chapter argument539    #540    if video_chapter is not None:541        chapter = '-chapter %d-%d' %(video_chapter,video_chapter)542    else:543        chapter = ''544#    chapter = '-chapter 2-2'545    #546    # build audio_filter argument547    #548    audio_filter = ''549    if audio_sample_rate:550        if audio_filter != '':551            audio_filter = audio_filter + ','552        audio_filter = audio_filter + 'lavcresample=%s' % audio_sample_rate 553    if audio_volume_boost is not None:554        if audio_filter != '':555            audio_filter = audio_filter + ','556        audio_filter = audio_filter + 'volume=%0.1f:1'%audio_volume_boost557    if audio_filter != '':558        audio_filter = '-af ' + audio_filter559    #560    #if audio_sample_rate:561    #    audio_filter = ('-srate %d ' % audio_sample_rate) + audio_filter562    #563    # build lavcopts argument564    #565    #lavcopts = '-lavcopts vcodec=%s:vbitrate=%d:mbd=2:aspect=%s:acodec=%s:abitrate=%d:vpass=1' % (video_codec,video_bitrate,audio_codec,audio_bitrate)566    lavcopts = '-lavcopts vcodec=%(video_codec)s:vbitrate=%(video_bitrate)d:mbd=2:aspect=%(video_aspect_ratio)s:acodec=%(audio_codec)s:abitrate=%(audio_bitrate)d:vpass=1' % (locals())567    if video_gray_flag:568        lavcopts = lavcopts + ':gray'569    seek_filter = ''570    if seek_skip is not None:571        seek_filter = '-ss %s' % (str(seek_skip))572    if seek_length is not None:573        seek_filter = seek_filter + ' -endpos %s' % (str(seek_length))574    cmd = "mencoder -quiet -info comment='Arkivist' '%(video_source_filename)s' %(seek_filter)s %(chapter)s -aid %(audio_id)s -o '%(video_final_filename)s' -ffourcc %(video_fourcc_override)s -ovc lavc -oac lavc %(lavcopts)s %(video_filter)s %(audio_filter)s" % locals()575    return cmd576def compression_estimate (video_length, video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None):577    """This attempts to figure out the best compression ratio for a given set of compression options.578    """579    # TODO Need to account for AVI overhead.580    skip = int(video_length/9) # offset to skip (-ss option in mencoder)581    sample_length = 10582    cmd1 = build_compression_command (video_source_filename, "compression_test_1.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip, sample_length)583    cmd2 = build_compression_command (video_source_filename, "compression_test_2.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*2, sample_length)584    cmd3 = build_compression_command (video_source_filename, "compression_test_3.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*4, sample_length)585    cmd4 = build_compression_command (video_source_filename, "compression_test_4.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*6, sample_length)586    cmd5 = build_compression_command (video_source_filename, "compression_test_5.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*8, sample_length)587    run(cmd1)588    run(cmd2)589    run(cmd3)590    run(cmd4)591    run(cmd5)592    size = get_filesize ("compression_test_1.avi")+get_filesize ("compression_test_2.avi")+get_filesize ("compression_test_3.avi")+get_filesize ("compression_test_4.avi")+get_filesize ("compression_test_5.avi")593    return (size / 5.0)594def compress_video (video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None, seek_skip=None, seek_length=None, video_chapter=None, verbose_flag=0, dry_run_flag=0):595    """This compresses the video and audio of the given source video filename to the transcoded filename.596        This does a two-pass compression (I'm assuming mpeg4, I should probably make this smarter for other formats).597    """598    #599    # do the first pass video compression600    #601    #cmd = "mencoder -quiet '%(video_source_filename)s' -ss 65 -endpos 20 -aid %(audio_id)s -o '%(video_final_filename)s' -ffourcc %(video_fourcc_override)s -ovc lavc -oac lavc %(lavcopts)s %(video_filter)s %(audio_filter)s" % locals()602    cmd = build_compression_command (video_source_filename, video_final_filename, video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, seek_skip, seek_length, video_chapter)603    if verbose_flag: print cmd604    if not dry_run_flag:605        run(cmd)606        print607    # If not doing two passes then return early.608    if video_encode_passes!='2':609        return610    if verbose_flag:611        video_actual_size = get_filesize (video_final_filename)612        if video_actual_size > video_target_size:613            print "======================================================="614            print "WARNING!"615            print "First pass compression resulted in"616            print "actual file size greater than target size."617            print "Second pass will be too big."618            print "======================================================="619    #620    # do the second pass video compression621    #622    cmd = cmd.replace ('vpass=1', 'vpass=2')623    if verbose_flag: print cmd624    if not dry_run_flag:625        run(cmd)626        print627    return628def compress_audio (audio_raw_filename, audio_compressed_filename, audio_lowpass_filter=None, audio_sample_rate=None, audio_bitrate=None, verbose_flag=0, dry_run_flag=0):629    """This is depricated.630    This compresses the raw audio file to the compressed audio filename.631    """632    cmd = 'lame -h --athaa-sensitivity 1' # --cwlimit 11"633    if audio_lowpass_filter:634        cmd = cmd + ' --lowpass ' + audio_lowpass_filter635    if audio_bitrate:636        #cmd = cmd + ' --abr ' + audio_bitrate637        cmd = cmd + ' --cbr -b ' + audio_bitrate638    if audio_sample_rate:639        cmd = cmd + ' --resample ' + audio_sample_rate640    cmd = cmd + ' ' + audio_raw_filename + ' ' + audio_compressed_filename641    if verbose_flag: print cmd642    if not dry_run_flag:643        (command_output, exitstatus) = run(cmd)644        print645        if exitstatus != 0:646            raise Exception('ERROR: lame failed to compress raw audio file.')647def mux (video_final_filename, video_transcoded_filename, audio_compressed_filename, video_container_format, verbose_flag=0, dry_run_flag=0):648    """This is depricated. I used to use a three-pass encoding where I would mix the audio track separately, but649    this never worked very well (loss of audio sync)."""650    if video_container_format.lower() == 'mkv': # Matroska651        mux_mkv (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag, dry_run_flag)652    if video_container_format.lower() == 'avi':653        mux_avi (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag, dry_run_flag)654def mux_mkv (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag=0, dry_run_flag=0):655    """This is depricated."""656    cmd = 'mkvmerge -o %s --noaudio %s %s' % (video_final_filename, video_transcoded_filename, audio_compressed_filename)657    if verbose_flag: print cmd658    if not dry_run_flag:659        run(cmd)660        print661def mux_avi (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag=0, dry_run_flag=0):662    """This is depricated."""663    cmd = "mencoder -quiet -oac copy -ovc copy -o '%s' -audiofile %s '%s'" % (video_final_filename, audio_compressed_filename, video_transcoded_filename)664    if verbose_flag: print cmd665    if not dry_run_flag:666        run(cmd)667        print668def delete_tmp_files (audio_raw_filename, verbose_flag=0, dry_run_flag=0):669    global GLOBAL_LOGFILE_NAME670    file_list = ' '.join([GLOBAL_LOGFILE_NAME, 'divx2pass.log', audio_raw_filename ])671    cmd = 'rm -f ' + file_list672    if verbose_flag: print cmd673    if not dry_run_flag:674        run(cmd)675        print676    677##############################################################################678# This is the interactive Q&A that is used if a conf file was not given.679##############################################################################680def interactive_convert ():681    global prompts, prompts_key_order682    print globals()['__doc__']683    print684    print "=============================================="685    print " Enter '?' at any question to get extra help."686    print "=============================================="687    print688  689    # Ask for the level of options the user wants. 690    # A lot of code just to print a string!691    level_sort = {0:'', 1:'', 2:''} 692    for k in prompts:693        level = prompts[k][3]694        if level < 0 or level > 2:695            continue696        level_sort[level] += "    " + prompts[k][1] + "\n"697    level_sort_string = "This sets the level for advanced options prompts. Set 0 for simple, 1 for advanced, or 2 for expert.\n"698    level_sort_string += "[0] Basic options:\n" + str(level_sort[0]) + "\n"699    level_sort_string += "[1] Advanced options:\n" + str(level_sort[1]) + "\n"700    level_sort_string += "[2] Expert options:\n" + str(level_sort[2])701    c = input_option("Prompt level (0, 1, or 2)?", "1", level_sort_string)702    max_prompt_level = int(c)703    options = {}704    for k in prompts_key_order:705        if k == 'video_aspect_ratio':706            guess_aspect = get_aspect_ratio(options['video_source_filename'])707            options[k] = input_option (prompts[k][1], guess_aspect, prompts[k][2], prompts[k][3], max_prompt_level)708        elif k == 'audio_id':709            aid_list = get_aid_list (options['video_source_filename'])710            default_id = '128'711            if max_prompt_level>=prompts[k][3]: 712                if len(aid_list) > 1:713                    print "This video has more than one audio stream. The following stream audio IDs were found:"714                    for aid in aid_list:715                        print "    " + aid716                    default_id = aid_list[0]717                else:718                    print "WARNING!"719                    print "Rippy was unable to get the list of audio streams from this video."720                    print "If reading directly from a DVD then the DVD device might be busy."721                    print "Using a default setting of stream id 128 (main audio on most DVDs)."722                    default_id = '128'723            options[k] = input_option (prompts[k][1], default_id, prompts[k][2], prompts[k][3], max_prompt_level)724        elif k == 'subtitle_id':725            sid_list = get_sid_list (options['video_source_filename'])726            default_id = 'None'727            if max_prompt_level>=prompts[k][3]:728                if len(sid_list) > 0:729                    print "This video has one or more subtitle streams. The following stream subtitle IDs were found:"730                    for sid in sid_list:731                        print "    " + sid732                    #default_id = sid_list[0]733                    default_id = prompts[k][0]734                else:735                    print "WARNING!"736                    print "Unable to get the list of subtitle streams from this video. It may have none."737                    print "Setting default to None."738                    default_id = 'None'739            options[k] = input_option (prompts[k][1], default_id, prompts[k][2], prompts[k][3], max_prompt_level)740        elif k == 'audio_lowpass_filter':741            lowpass_default =  "%.1f" % (math.floor(float(options['audio_sample_rate']) / 2.0))742            options[k] = input_option (prompts[k][1], lowpass_default, prompts[k][2], prompts[k][3], max_prompt_level)743        elif k == 'video_bitrate':744            if options['video_length'].lower() == 'none':745                options[k] = input_option (prompts[k][1], '1000', prompts[k][2], prompts[k][3], max_prompt_level)746            else:747                options[k] = input_option (prompts[k][1], prompts[k][0], prompts[k][2], prompts[k][3], max_prompt_level)748        else:749            # don't bother asking for video_target_size or video_bitrate_overhead if video_bitrate was set750            if (k=='video_target_size' or k=='video_bitrate_overhead') and options['video_bitrate']!='calc':751                continue752            # don't bother with crop area if video length is none753            if k == 'video_crop_area' and options['video_length'].lower() == 'none':754                options['video_crop_area'] = 'none'755                continue756            options[k] = input_option (prompts[k][1], prompts[k][0], prompts[k][2], prompts[k][3], max_prompt_level)757    #options['video_final_filename'] = options['video_final_filename'] + "." + options['video_container_format']758    print "=========================================================================="759    print "Ready to Rippy!"760    print761    print "The following options will be used:"762    for k,v in options.iteritems():763        print "%27s : %s" % (k, v)764    print765    c = input_option("Continue?", "Y")766    c = c.strip().lower()767    if c[0] != 'y':768        print "Exiting..."769        os._exit(1)770    return options771def clean_options (d):772    """This validates and cleans up the options dictionary.773    After reading options interactively or from a conf file774    we need to make sure that the values make sense and are775    converted to the correct type.776    1. Any key with "_flag" in it becomes a boolean True or False.777    2. Values are normalized ("No", "None", "none" all become "none";778    "Calcluate", "c", "CALC" all become "calc").779    3. Certain values are converted from string to int.780    4. Certain combinations of options are invalid or override each other.781    This is a rather annoying function, but then so it most cleanup work.782    """783    for k in d:784        d[k] = d[k].strip()785        # convert all flag options to 0 or 1786        if '_flag' in k:787            if type(d[k]) is types.StringType:788                if d[k].strip().lower()[0] in 'yt1': #Yes, True, 1789                    d[k] = 1790                else:791                    d[k] = 0792    d['video_bitrate'] = d['video_bitrate'].lower()793    if d['video_bitrate'][0]=='c':794        d['video_bitrate']='calc'795    else:796        d['video_bitrate'] = int(float(d['video_bitrate']))797    try:798        d['video_target_size'] = int(d['video_target_size'])799        # shorthand magic numbers get automatically expanded800        if d['video_target_size'] == 180:801            d['video_target_size'] = 193536000802        elif d['video_target_size'] == 550:803            d['video_target_size'] = 580608000804        elif d['video_target_size'] == 650:805            d['video_target_size'] = 681984000806        elif d['video_target_size'] == 700:807            d['video_target_size'] = 737280000808    except:809        d['video_target_size'] = 'none'810    try:811        d['video_chapter'] = int(d['video_chapter'])812    except:813        d['video_chapter'] = None814    try:815        d['subtitle_id'] = int(d['subtitle_id'])816    except:817        d['subtitle_id'] = None818        819    try:820        d['video_bitrate_overhead'] = float(d['video_bitrate_overhead'])821    except:822        d['video_bitrate_overhead'] = -1.0823    d['audio_bitrate'] = int(d['audio_bitrate'])824    d['audio_sample_rate'] = int(d['audio_sample_rate'])825    d['audio_volume_boost'] = d['audio_volume_boost'].lower()826    if d['audio_volume_boost'][0]=='n':827        d['audio_volume_boost'] = None828    else:829        d['audio_volume_boost'] = d['audio_volume_boost'].replace('db','')830        d['audio_volume_boost'] = float(d['audio_volume_boost'])831#    assert (d['video_bitrate']=='calc' and d['video_target_size']!='none')832# or (d['video_bitrate']!='calc' and d['video_target_size']=='none')833    d['video_scale'] = d['video_scale'].lower()834    if d['video_scale'][0]=='n':835        d['video_scale']='none'836    else:837        al = re.findall("([0-9]+).*?([0-9]+)", d['video_scale'])838        d['video_scale']=al[0][0]+':'+al[0][1]839    d['video_crop_area'] = d['video_crop_area'].lower()840    if d['video_crop_area'][0]=='n':841        d['video_crop_area']='none'842    d['video_length'] = d['video_length'].lower()843    if d['video_length'][0]=='c':844        d['video_length']='calc'845    elif d['video_length'][0]=='n':846        d['video_length']='none'847    else:848        d['video_length'] = int(float(d['video_length']))849    if d['video_length']==0:850        d['video_length'] = 'none'851    assert (not (d['video_length']=='none' and d['video_bitrate']=='calc'))852    return d853def main ():854    try:855        optlist, args = getopt.getopt(sys.argv[1:], 'h?', ['help','h','?'])856    except Exception, e:857        print str(e)858        exit_with_usage()859    command_line_options = dict(optlist)860    # There are a million ways to cry for help. These are but a few of them.861    if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]:862        exit_with_usage(0)863    missing = check_missing_requirements()864    if missing is not None:865        print866        print "=========================================================================="867        print "ERROR!"868        print "Some required external commands are missing."869        print "please install the following packages:"870        print str(missing)871        print "=========================================================================="872        print873        c = input_option("Continue?", "Y")874        c = c.strip().lower()875        if c[0] != 'y':876            print "Exiting..."877            os._exit(1)878    if len(args) > 0:879        # cute one-line string-to-dictionary parser (two-lines if you count this comment):880        options = dict(re.findall('([^: \t\n]*)\s*:\s*(".*"|[^ \t\n]*)', file(args[0]).read()))881        options = clean_options(options)882        convert (options)883    else:884        options = interactive_convert ()885        options = clean_options(options)886        convert (options)887    print "# Done!"888    889if __name__ == "__main__":890    try:891        start_time = time.time()892        print time.asctime()893        main()894        print time.asctime()895        print "TOTAL TIME IN MINUTES:",896        print (time.time() - start_time) / 60.0897    except Exception, e:898        tb_dump = traceback.format_exc()899        print "=========================================================================="900        print "ERROR -- Unexpected exception in script."901        print str(e)902        print str(tb_dump)903        print "=========================================================================="904        print >>GLOBAL_LOGFILE, "=========================================================================="905        print >>GLOBAL_LOGFILE, "ERROR -- Unexpected exception in script."906        print >>GLOBAL_LOGFILE, str(e)907        print >>GLOBAL_LOGFILE, str(tb_dump)908        print >>GLOBAL_LOGFILE, "=========================================================================="...video_keyframe_dataset.py
Source:video_keyframe_dataset.py  
1# -*- coding: utf-8 -*-2# Copyright (c) Facebook, Inc. and its affiliates.3import csv4import logging5import numpy as np6from typing import Any, Callable, Dict, List, Optional, Union7import av8import torch9from torch.utils.data.dataset import Dataset10from detectron2.utils.file_io import PathManager11from ..utils import maybe_prepend_base_path12from .frame_selector import FrameSelector, FrameTsList13FrameList = List[av.frame.Frame]  # pyre-ignore[16]14FrameTransform = Callable[[torch.Tensor], torch.Tensor]15def list_keyframes(video_fpath: str, video_stream_idx: int = 0) -> FrameTsList:16    """17    Traverses all keyframes of a video file. Returns a list of keyframe18    timestamps. Timestamps are counts in timebase units.19    Args:20       video_fpath (str): Video file path21       video_stream_idx (int): Video stream index (default: 0)22    Returns:23       List[int]: list of keyframe timestaps (timestamp is a count in timebase24           units)25    """26    try:27        with PathManager.open(video_fpath, "rb") as io:28            container = av.open(io, mode="r")29            stream = container.streams.video[video_stream_idx]30            keyframes = []31            pts = -132            # Note: even though we request forward seeks for keyframes, sometimes33            # a keyframe in backwards direction is returned. We introduce tolerance34            # as a max count of ignored backward seeks35            tolerance_backward_seeks = 236            while True:37                try:38                    container.seek(pts + 1, backward=False, any_frame=False, stream=stream)39                except av.AVError as e:40                    # the exception occurs when the video length is exceeded,41                    # we then return whatever data we've already collected42                    logger = logging.getLogger(__name__)43                    logger.debug(44                        f"List keyframes: Error seeking video file {video_fpath}, "45                        f"video stream {video_stream_idx}, pts {pts + 1}, AV error: {e}"46                    )47                    return keyframes48                except OSError as e:49                    logger = logging.getLogger(__name__)50                    logger.warning(51                        f"List keyframes: Error seeking video file {video_fpath}, "52                        f"video stream {video_stream_idx}, pts {pts + 1}, OS error: {e}"53                    )54                    return []55                packet = next(container.demux(video=video_stream_idx))56                if packet.pts is not None and packet.pts <= pts:57                    logger = logging.getLogger(__name__)58                    logger.warning(59                        f"Video file {video_fpath}, stream {video_stream_idx}: "60                        f"bad seek for packet {pts + 1} (got packet {packet.pts}), "61                        f"tolerance {tolerance_backward_seeks}."62                    )63                    tolerance_backward_seeks -= 164                    if tolerance_backward_seeks == 0:65                        return []66                    pts += 167                    continue68                tolerance_backward_seeks = 269                pts = packet.pts70                if pts is None:71                    return keyframes72                if packet.is_keyframe:73                    keyframes.append(pts)74            return keyframes75    except OSError as e:76        logger = logging.getLogger(__name__)77        logger.warning(78            f"List keyframes: Error opening video file container {video_fpath}, " f"OS error: {e}"79        )80    except RuntimeError as e:81        logger = logging.getLogger(__name__)82        logger.warning(83            f"List keyframes: Error opening video file container {video_fpath}, "84            f"Runtime error: {e}"85        )86    return []87def read_keyframes(88    video_fpath: str, keyframes: FrameTsList, video_stream_idx: int = 089) -> FrameList:  # pyre-ignore[11]90    """91    Reads keyframe data from a video file.92    Args:93        video_fpath (str): Video file path94        keyframes (List[int]): List of keyframe timestamps (as counts in95            timebase units to be used in container seek operations)96        video_stream_idx (int): Video stream index (default: 0)97    Returns:98        List[Frame]: list of frames that correspond to the specified timestamps99    """100    try:101        with PathManager.open(video_fpath, "rb") as io:102            container = av.open(io)103            stream = container.streams.video[video_stream_idx]104            frames = []105            for pts in keyframes:106                try:107                    container.seek(pts, any_frame=False, stream=stream)108                    frame = next(container.decode(video=0))109                    frames.append(frame)110                except av.AVError as e:111                    logger = logging.getLogger(__name__)112                    logger.warning(113                        f"Read keyframes: Error seeking video file {video_fpath}, "114                        f"video stream {video_stream_idx}, pts {pts}, AV error: {e}"115                    )116                    container.close()117                    return frames118                except OSError as e:119                    logger = logging.getLogger(__name__)120                    logger.warning(121                        f"Read keyframes: Error seeking video file {video_fpath}, "122                        f"video stream {video_stream_idx}, pts {pts}, OS error: {e}"123                    )124                    container.close()125                    return frames126                except StopIteration:127                    logger = logging.getLogger(__name__)128                    logger.warning(129                        f"Read keyframes: Error decoding frame from {video_fpath}, "130                        f"video stream {video_stream_idx}, pts {pts}"131                    )132                    container.close()133                    return frames134            container.close()135            return frames136    except OSError as e:137        logger = logging.getLogger(__name__)138        logger.warning(139            f"Read keyframes: Error opening video file container {video_fpath}, OS error: {e}"140        )141    except RuntimeError as e:142        logger = logging.getLogger(__name__)143        logger.warning(144            f"Read keyframes: Error opening video file container {video_fpath}, Runtime error: {e}"145        )146    return []147def video_list_from_file(video_list_fpath: str, base_path: Optional[str] = None):148    """149    Create a list of paths to video files from a text file.150    Args:151        video_list_fpath (str): path to a plain text file with the list of videos152        base_path (str): base path for entries from the video list (default: None)153    """154    video_list = []155    with PathManager.open(video_list_fpath, "r") as io:156        for line in io:157            video_list.append(maybe_prepend_base_path(base_path, str(line.strip())))158    return video_list159def read_keyframe_helper_data(fpath: str):160    """161    Read keyframe data from a file in CSV format: the header should contain162    "video_id" and "keyframes" fields. Value specifications are:163      video_id: int164      keyframes: list(int)165    Example of contents:166      video_id,keyframes167      2,"[1,11,21,31,41,51,61,71,81]"168    Args:169        fpath (str): File containing keyframe data170    Return:171        video_id_to_keyframes (dict: int -> list(int)): for a given video ID it172          contains a list of keyframes for that video173    """174    video_id_to_keyframes = {}175    try:176        with PathManager.open(fpath, "r") as io:177            csv_reader = csv.reader(io)  # pyre-ignore[6]178            header = next(csv_reader)179            video_id_idx = header.index("video_id")180            keyframes_idx = header.index("keyframes")181            for row in csv_reader:182                video_id = int(row[video_id_idx])183                assert (184                    video_id not in video_id_to_keyframes185                ), f"Duplicate keyframes entry for video {fpath}"186                video_id_to_keyframes[video_id] = (187                    [int(v) for v in row[keyframes_idx][1:-1].split(",")]188                    if len(row[keyframes_idx]) > 2189                    else []190                )191    except Exception as e:192        logger = logging.getLogger(__name__)193        logger.warning(f"Error reading keyframe helper data from {fpath}: {e}")194    return video_id_to_keyframes195class VideoKeyframeDataset(Dataset):196    """197    Dataset that provides keyframes for a set of videos.198    """199    _EMPTY_FRAMES = torch.empty((0, 3, 1, 1))200    def __init__(201        self,202        video_list: List[str],203        category_list: Union[str, List[str], None] = None,204        frame_selector: Optional[FrameSelector] = None,205        transform: Optional[FrameTransform] = None,206        keyframe_helper_fpath: Optional[str] = None,207    ):208        """209        Dataset constructor210        Args:211            video_list (List[str]): list of paths to video files212            category_list (Union[str, List[str], None]): list of animal categories for each213                video file. If it is a string, or None, this applies to all videos214            frame_selector (Callable: KeyFrameList -> KeyFrameList):215                selects keyframes to process, keyframes are given by216                packet timestamps in timebase counts. If None, all keyframes217                are selected (default: None)218            transform (Callable: torch.Tensor -> torch.Tensor):219                transforms a batch of RGB images (tensors of size [B, 3, H, W]),220                returns a tensor of the same size. If None, no transform is221                applied (default: None)222        """223        if type(category_list) == list:224            self.category_list = category_list225        else:226            self.category_list = [category_list] * len(video_list)227        assert len(video_list) == len(228            self.category_list229        ), "length of video and category lists must be equal"230        self.video_list = video_list231        self.frame_selector = frame_selector232        self.transform = transform233        self.keyframe_helper_data = (234            read_keyframe_helper_data(keyframe_helper_fpath)235            if keyframe_helper_fpath is not None236            else None237        )238    def __getitem__(self, idx: int) -> Dict[str, Any]:239        """240        Gets selected keyframes from a given video241        Args:242            idx (int): video index in the video list file243        Returns:244            A dictionary containing two keys:245                images (torch.Tensor): tensor of size [N, H, W, 3] or of size246                    defined by the transform that contains keyframes data247                categories (List[str]): categories of the frames248        """249        categories = [self.category_list[idx]]250        fpath = self.video_list[idx]251        keyframes = (252            list_keyframes(fpath)253            if self.keyframe_helper_data is None or idx not in self.keyframe_helper_data254            else self.keyframe_helper_data[idx]255        )256        transform = self.transform257        frame_selector = self.frame_selector258        if not keyframes:259            return {"images": self._EMPTY_FRAMES, "categories": []}260        if frame_selector is not None:261            keyframes = frame_selector(keyframes)262        frames = read_keyframes(fpath, keyframes)263        if not frames:264            return {"images": self._EMPTY_FRAMES, "categories": []}265        frames = np.stack([frame.to_rgb().to_ndarray() for frame in frames])266        frames = torch.as_tensor(frames, device=torch.device("cpu"))267        frames = frames[..., [2, 1, 0]]  # RGB -> BGR268        frames = frames.permute(0, 3, 1, 2).float()  # NHWC -> NCHW269        if transform is not None:270            frames = transform(frames)271        return {"images": frames, "categories": categories}272    def __len__(self):...videointelligenceml.py
Source:videointelligenceml.py  
...116    self.timeout = timeout117    self.counter = Metrics.counter(self.__class__, "API Calls")118  def start_bundle(self):119    self._client = get_videointelligence_client()120  def _annotate_video(self, element, video_context):121    if isinstance(element, text_type):  # Is element an URI to a GCS bucket122      response = self._client.annotate_video(123          input_uri=element,124          features=self.features,125          video_context=video_context,126          location_id=self.location_id,127          metadata=self.metadata)128    else:  # Is element raw bytes129      response = self._client.annotate_video(130          input_content=element,131          features=self.features,132          video_context=video_context,133          location_id=self.location_id,134          metadata=self.metadata)135    return response136  def process(self, element, context_side_input=None, *args, **kwargs):137    if context_side_input:  # If we have a side input video context, use that138      video_context = context_side_input.get(element)139    else:140      video_context = None141    response = self._annotate_video(element, video_context)142    self.counter.inc()143    yield response.result(timeout=self.timeout)144class AnnotateVideoWithContext(AnnotateVideo):145  """A ``PTransform`` for annotating video using the GCP Video Intelligence API146  ref: https://cloud.google.com/video-intelligence/docs147  Sends each element to the GCP Video Intelligence API.148  Element is a tuple of149    (Union[text_type, binary_type],150    Optional[videointelligence.types.VideoContext])151  where the former is either an URI (e.g. a GCS URI) or152  binary_type base64-encoded video data153  """154  def __init__(self, features, location_id=None, metadata=None, timeout=120):155    """156      Args:157        features: (List[``videointelligence_v1.enums.Feature``]) Required.158          the Video Intelligence API features to detect159        location_id: (str) Optional.160          Cloud region where annotation should take place.161          If no region is specified, a region will be determined162          based on video file location.163        metadata: (Sequence[Tuple[str, str]]) Optional.164          Additional metadata that is provided to the method.165        timeout: (int) Optional.166          The time in seconds to wait for the response from the167          Video Intelligence API168    """169    super(AnnotateVideoWithContext, self).__init__(170        features=features,171        location_id=location_id,172        metadata=metadata,173        timeout=timeout)174  def expand(self, pvalue):175    return pvalue | ParDo(176        _VideoAnnotateFnWithContext(177            features=self.features,178            location_id=self.location_id,179            metadata=self.metadata,180            timeout=self.timeout))181@typehints.with_input_types(182    Tuple[Union[text_type, binary_type],183          Optional[videointelligence.types.VideoContext]])184class _VideoAnnotateFnWithContext(_VideoAnnotateFn):185  """A DoFn that unpacks each input tuple to element, video_context variables186  and sends these to the GCP Video Intelligence API service and outputs187  an element with the return result of the API188  (``google.cloud.videointelligence_v1.types.AnnotateVideoResponse``).189  """190  def __init__(self, features, location_id, metadata, timeout):191    super(_VideoAnnotateFnWithContext, self).__init__(192        features=features,193        location_id=location_id,194        metadata=metadata,195        timeout=timeout)196  def process(self, element, *args, **kwargs):197    element, video_context = element  # Unpack (video, video_context) tuple198    response = self._annotate_video(element, video_context)199    self.counter.inc()...app.py
Source:app.py  
1from flask import Flask, render_template, Response, request, jsonify, flash2import cv23from handDetect import capture_hand4from pen_version2 import board_gen, clear_board5from haze_removal import HazeRemoval6app = Flask(__name__)7app.secret_key = 'dont tell anyone'8video_camera = None9global_frame = None10record = False11palmCascade = cv2.CascadeClassifier('palm_cascade.xml')12message = 'Not allowed to speak yet'13canvas = None14@app.route('/')15def stream():16    global video_camera17    if video_camera is not None:18        video_camera.release()19        cv2.destroyAllWindows()20    video_camera = cv2.VideoCapture(0)21    return render_template('index.html')22@app.route('/applause')23def applause():24    global video_camera25    if video_camera is not None:26        video_camera.release()27        cv2.destroyAllWindows()28    video_camera = cv2.VideoCapture(0)29    return render_template('applause.html')30@app.route('/board')31def board():32    global video_camera33    if video_camera is not None:34        video_camera.release()35        cv2.destroyAllWindows()36    video_camera = cv2.VideoCapture(0)37    return render_template('board.html')38@app.route('/refining')39def refining():40    global video_camera41    if video_camera is not None:42        video_camera.release()43        cv2.destroyAllWindows()44    video_camera = cv2.VideoCapture(0)45    return render_template('refining.html')46def create_message():47    global message48    yield message49@app.route('/my_message')50def my_message():51    return Response(create_message())52def generate_frame():53    global video_camera54    global global_frame55    global record56    global palmCascade57    global message58    record = not record59    if video_camera is None and record is True:60        video_camera = cv2.VideoCapture(0)61    while record is True:62        # get camera frame63        # ret, frame = video_camera.read()64        hand_flag, frame = capture_hand(video_capture=video_camera, palmCascade=palmCascade)65        if frame is not None:66            # frame = cv2.flip(frame, 1)67            ret, frame = cv2.imencode('.jpg', frame)68            frame = frame.tobytes()69            global_frame = frame70            # flash(message)71            yield b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + global_frame + b'\r\n\r\n'72        else:73            yield b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + global_frame + b'\r\n\r\n'74        # if hand_flag:75        # video_camera.release()76@app.route('/record_status', methods=['POST'])77def record_status():78    global video_camera79    json = request.get_json()80    status = json['status']81    if status == "true":82        video_camera = cv2.VideoCapture(0)83        return jsonify(result="started")84    else:85        video_camera.release()86        return jsonify(result="stopped")87@app.route('/video_feed')88def video_feed():89    return Response(generate_frame(), mimetype='multipart/x-mixed-replace; boundary=frame')90def generate_board():91    global video_camera92    global global_frame93    global record94    global message95    global canvas96    record = not record97    if video_camera is None and record is True:98        video_camera = cv2.VideoCapture(0)99    while record is True:100        # get camera frame101        # ret, frame = video_camera.read()102        _, frame = video_camera.read()103        frame = board_gen(video_camera, canvas, frame)104        if frame is not None:105            # frame = cv2.flip(frame, 1)106            ret, frame = cv2.imencode('.jpg', frame)107            frame = frame.tobytes()108            global_frame = frame109            # flash(message)110            yield b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + global_frame + b'\r\n\r\n'111        else:112            yield b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + global_frame + b'\r\n\r\n'113        # if hand_flag:114        # video_camera.release()115@app.route('/board_feed')116def board_feed():117    return Response(generate_board(), mimetype='multipart/x-mixed-replace; boundary=frame')118@app.route('/clear', methods=['GET'])119def clear():120    clear_board()121def generate_refined_frame():122    global video_camera123    global global_frame124    global record125    global palmCascade126    global message127    record = not record128    if video_camera is None and record is True:129        video_camera = cv2.VideoCapture(0)130    while record is True:131        # get camera frame132        # ret, frame = video_camera.read()133        _, frame = video_camera.read()134        if frame is not None:135            # frame = cv2.imencode('.jpg', frame)136            frame = cv2.flip(frame, 1)137            hr = HazeRemoval()138            hr.open_image(frame)139            hr.get_dark_channel()140            hr.get_air_light()141            hr.get_transmission()142            hr.guided_filter()143            hr.recover()144            frame = hr.show()145        if frame is not None:146            # frame = cv2.flip(frame, 1)147            ret, frame = cv2.imencode('.jpg', frame)148            frame = frame.tobytes()149            global_frame = frame150            # flash(message)151            yield b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + global_frame + b'\r\n\r\n'152        else:153            yield b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + global_frame + b'\r\n\r\n'154        # if hand_flag:155        # video_camera.release()156@app.route('/refined_video_feed')157def refined_video_feed():158    return Response(generate_refined_frame(), mimetype='multipart/x-mixed-replace; boundary=frame')159if __name__ == '__main__':...LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!
