src/pyams_media/ffbase.py
changeset 0 fd39db613f8b
child 7 b930c820b6cd
equal deleted inserted replaced
-1:000000000000 0:fd39db613f8b
       
     1 #
       
     2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
       
     3 # All Rights Reserved.
       
     4 #
       
     5 # This software is subject to the provisions of the Zope Public License,
       
     6 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    10 # FOR A PARTICULAR PURPOSE.
       
    11 #
       
    12 
       
    13 # import standard library
       
    14 import json
       
    15 import logging
       
    16 logger = logging.getLogger('PyAMS (media)')
       
    17 
       
    18 import mimetypes
       
    19 import re
       
    20 import tempfile
       
    21 
       
    22 from os.path import dirname, basename
       
    23 from os import sep, remove
       
    24 from subprocess import Popen, PIPE
       
    25 
       
    26 # import interfaces
       
    27 from pyams_file.interfaces import IFile
       
    28 
       
    29 # import packages
       
    30 from pyams_file.file import get_magic_content_type
       
    31 from pyams_media.ffexception import FFException
       
    32 
       
    33 
       
    34 __all__ = ['FFmpeg', 'FFVideoEffect', 'FFAudioEffect']
       
    35 
       
    36 
       
    37 INPUT_BLOCK_SIZE = 1024 ** 2
       
    38 
       
    39 
       
    40 class FFmpeg(object):
       
    41     """
       
    42     FFmpeg Wrapper
       
    43     """
       
    44 
       
    45     # thanks to pyxcoder http://code.google.com/p/pyxcoder for
       
    46     # the main idea
       
    47     re_mainline = re.compile("^\s*Input #(\d+?), (.*?), from \'(.*?)\':$")
       
    48     re_infoline = re.compile("^\s*Duration: (.*?), start: 0\.000000, bitrate: (\d+?) kb\/s$")
       
    49     re_videoline = re.compile("^\s*Stream #(\d+:\d+?)\(?([A-Za-z]*)\)?: Video: (.*?), (.*?), (.*?), (.*?)$")
       
    50     re_audioline = re.compile("^\s*Stream #(\d+:\d+?)\(?([A-Za-z]*)\)?: Audio: (.*?), (\d+?) Hz, (.*?), (.*?), (\d+?) kb\/s$")
       
    51 
       
    52     def __init__(self, cmd="ffmpeg"):
       
    53         self.__ffmpeg__ = cmd
       
    54 
       
    55     def __exec__(self, *args):
       
    56         """Build and execute a command line"""
       
    57         cmdline = [self.__ffmpeg__]
       
    58         if self.__ffmpeg__ == 'ffmpeg':
       
    59             cmdline.append('-y')
       
    60         use_stdin = None
       
    61         for arg in args:
       
    62             if IFile.providedBy(arg):
       
    63                 if len(args) == 2:
       
    64                     # FFmpeg can't get media info from an input pipe
       
    65                     # We have to write media content to temporary file
       
    66                     suffix = '.tmp'
       
    67                     content_type = get_magic_content_type(arg.data)
       
    68                     if content_type:
       
    69                         suffix = mimetypes.guess_extension(content_type) or suffix
       
    70                     output = tempfile.NamedTemporaryFile(prefix='media_', suffix=suffix)
       
    71                     output.write(arg.data)
       
    72                     output.file.flush()
       
    73                     cmdline.append(output.name)
       
    74                 else:
       
    75                     use_stdin = arg
       
    76                     cmdline.append('-')
       
    77             elif hasattr(arg, 'read'):  # StringIO or any file like object
       
    78                 if len(args) == 2:
       
    79                     # FFmpeg can't get media info from an input pipe
       
    80                     # We have to write media content to temporary file
       
    81                     arg.reset()
       
    82                     content_type = get_magic_content_type(arg.read(4096))
       
    83                     suffix = mimetypes.guess_extension(content_type) if content_type else '.tmp'
       
    84                     output = tempfile.NamedTemporaryFile(prefix='media_', suffix=suffix)
       
    85                     try:
       
    86                         arg.reset()
       
    87                     except:
       
    88                         pass
       
    89                     data = arg.read(INPUT_BLOCK_SIZE)
       
    90                     while data:
       
    91                         output.write(data)
       
    92                         data = arg.read(INPUT_BLOCK_SIZE)
       
    93                     output.file.flush()
       
    94                     cmdline.append(output.name)
       
    95                 else:
       
    96                     use_stdin = arg
       
    97                     cmdline.append('-')
       
    98             else:
       
    99                 cmdline.append(arg)
       
   100         logger.debug("Running FFmpeg command line: {0}".format(cmdline))
       
   101         p = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE)
       
   102         if use_stdin is not None:
       
   103             if IFile.providedBy(use_stdin):
       
   104                 return p.communicate(use_stdin.data)
       
   105             else:
       
   106                 use_stdin.reset()
       
   107                 return p.communicate(use_stdin.read())
       
   108         else:
       
   109             return p.communicate()
       
   110 
       
   111     def render(self, effectchain, output):
       
   112         """Create a new file by chaining audio/video effects"""
       
   113         inputs = []
       
   114         cmds = [[]]
       
   115         outputs = []
       
   116         # we want to operate on more objects that use the same file
       
   117         # source, So, we have to split the effect chain in various
       
   118         # intermediate jobs, then rebuild all
       
   119         for index, effect in enumerate(effectchain):
       
   120             if index == 1 and not effect in inputs:
       
   121                 inputs.append(effect)
       
   122                 cmds[len(cmds)-1].append(effect)
       
   123             else:
       
   124                 outputs.append("%s%s%s-%s" % (dirname(output), sep, len(cmds), basename(output)))
       
   125                 cmds.append([])
       
   126                 input = []
       
   127         # prcessing intermediate outputs
       
   128         for index, output in enumerate(outputs):
       
   129             cmd = ["-y", ]
       
   130             cmd.extend(inputs[index].cmdline())
       
   131             cmd.append(output)
       
   132             self.__exec__(*cmd)
       
   133         # procesing final output
       
   134         cmd = ["-y", ]
       
   135         for index, output in enumerate(outputs):
       
   136             doc = FFEffect(output)
       
   137             if index == 0 and inputs[index].offset():
       
   138                 doc.offset(inputs[index].offset())
       
   139             cmd.extend(doc.cmdline())
       
   140         cmd.append(output)
       
   141         self.__exec__(*cmd)
       
   142         # removing intermediate outputs
       
   143         for tmp in outputs:
       
   144             remove(tmp)
       
   145 
       
   146     def info(self, input):
       
   147         """Retrieve file information parsing command output"""
       
   148         metadata = []
       
   149         if IFile.providedBy(input) or isinstance(input, str) or hasattr(input, 'read'):
       
   150             input = [input, ]
       
   151         for i in range(0, len(input) * 2, 2):
       
   152             input.insert(i, "-i")
       
   153         if self.__ffmpeg__ == 'ffprobe':
       
   154             input.extend(['-show_streams', '-print_format', 'json'])
       
   155             probe = self.__exec__(*input)[0]  # stdout
       
   156             metadata.extend(json.loads(probe.decode()).get('streams', []))
       
   157         else:
       
   158             lines = self.__exec__(*input)[1]  # stderr
       
   159             for line in lines.split(b'\n'):
       
   160                 if isinstance(line, bytes):
       
   161                     try:
       
   162                         line = line.decode()
       
   163                     except UnicodeDecodeError:
       
   164                         logger.debug("Unicode decode error: {0}".format(line))
       
   165                         continue
       
   166                 if FFmpeg.re_mainline.match(line):
       
   167                     clip, vtype, filename = FFmpeg.re_mainline.match(line).groups()
       
   168                     metadata.append({"vtype": vtype, "filename": filename, "video": [], "audio": []})
       
   169                 elif FFmpeg.re_infoline.match(line):
       
   170                     current = len(metadata) - 1
       
   171                     metadata[current]["duration"], metadata[current]["bitrate"] = FFmpeg.re_infoline.match(line).groups()
       
   172                 elif FFmpeg.re_audioline.match(line):
       
   173                     clip, lang, codec, freq, chan, freqbit, bitrate = FFmpeg.re_audioline.match(line).groups()
       
   174                     audiostream = {"codec": codec, "lang": lang, "freq": freq, "chan": chan, "freqbit": freqbit, "bitrate": bitrate}
       
   175                     metadata[len(metadata) - 1]["audio"].append(audiostream)
       
   176                 elif FFmpeg.re_videoline.match(line):
       
   177                     clip, lang, codec, pix_fmt, size, framerate = FFmpeg.re_videoline.match(line).groups()
       
   178                     size = size.split(" ")
       
   179                     videostream = {"codec": codec, "lang": lang, "pix_fmt": pix_fmt, "size": size, "framerate": framerate}
       
   180                     metadata[len(metadata) - 1]["video"].append(videostream)
       
   181         return metadata
       
   182 
       
   183 
       
   184 class FFEffect:
       
   185     """
       
   186         effect for a specified input file
       
   187         each "set" method has an unset_* method 
       
   188         to clear the effect of the former (e.g.
       
   189         crop() and unset_crop() ), and a general
       
   190         unset() method 
       
   191     """
       
   192 
       
   193     def __init__(self, inputfile, **args):
       
   194         self.__file__ = inputfile
       
   195         for opt in args.keys():
       
   196             if opt not in ["b", "vframes", "r", "s", "aspect", "croptop",
       
   197                            "cropbottom", "cropleft", "cropright", "padtop",
       
   198                            "padbottom", "padleft", "padright", "padcolor",
       
   199                            "vn", "bt", "maxrate", "minrate", "bufsize",
       
   200                            "vcodec", "sameq", "pass", "newvideo", "pix_fmt",
       
   201                            "sws_flag", "g", "intra", "vdt", "qscale",
       
   202                            "qmin", "qmax", "qdiff", "qblur", "qcomp", "lmin",
       
   203                            "lmax", "mblmin", "mblmax", "rc_init_cplx",
       
   204                            "b_qfactor", "i_qfactor", "b_qoffset",
       
   205                            "i_qoffset", "rc_eq", "rc_override", "me_method",
       
   206                            "dct_algo", "idct_algo", "er", "ec", "bf", "mbd",
       
   207                            "4mv", "part", "bug", "strict", "aic", "umv",
       
   208                            "deinterlace", "ilme", "psnr", "vhook", "top",
       
   209                            "dc", "vtag", "vbsf", "aframes", "ar", "ab", "ac",
       
   210                            "an", "acodec", "newaudio", "alang", "t",
       
   211                            "itsoffset", "ss", "dframes"]:
       
   212                 raise FFException("Error parsing option: %s" % opt)
       
   213         self.__effects__ = args
       
   214         self.__default__ = self.__effects__.copy()
       
   215 
       
   216     def cmdline(self):
       
   217         """ return a list of arguments """
       
   218         cmd = ["-i", self.__file__]
       
   219         for opt, value in self.__effects__.items():
       
   220             cmd.append("-%s" % opt)
       
   221             if value is not True:
       
   222                 cmd.append("%s" % value)
       
   223         return cmd
       
   224 
       
   225     def get_output(self, format=None, target='-', get_stderr=False):
       
   226         if (format is None) and hasattr(self, '__metadata__'):
       
   227             format = self.__metadata__.get('vtype')
       
   228         stdout, stderr = FFmpeg().__exec__(*self.cmdline() + ['-f', format, target])
       
   229         return (stdout, stderr) if get_stderr else stdout
       
   230 
       
   231     def restore(self):
       
   232         """
       
   233             restore initial settings
       
   234         """
       
   235         self.__effects__ = self.__default__.copy()
       
   236 
       
   237     def unset(self):
       
   238         """
       
   239             clear settings
       
   240         """
       
   241         self.__effects__ = {}
       
   242 
       
   243     def duration(self, t=None):
       
   244         """ restrict transcode sequence to duration specified """
       
   245         if t:
       
   246             self.__effects__["t"] = float(t)
       
   247         return self.__effects__.get("t")
       
   248 
       
   249     def unset_duration(self):
       
   250         del self.__effects__["duration"]
       
   251 
       
   252     def seek(self, ss=None):
       
   253         """ seek to time position in seconds """
       
   254         if ss:
       
   255             self.__effects__["ss"] = float(ss)
       
   256         return self.__effects__.get("ss")
       
   257 
       
   258     def unset_seek(self):
       
   259         del self.__effects__["ss"]
       
   260 
       
   261     def offset(self, itsoffset=None):
       
   262         """ Set the input time offset in seconds """
       
   263         if itsoffset:
       
   264             self.__effects__["itsoffset"] = itsoffset
       
   265         return self.__effects__.get("itsoffset")
       
   266 
       
   267     def unset_offset(self):
       
   268         del self.__effects__["itsoffset"]
       
   269 
       
   270     def dframes(self, dframes=None):
       
   271         """ number of data frames to record """
       
   272         if dframes:
       
   273             self.__effects__["dframes"] = dframes
       
   274         return self.__effects__.get("dframes")
       
   275 
       
   276     def unset_dframes(self):
       
   277         del self.__effects__["dframes"]
       
   278 
       
   279 
       
   280 class FFVideoEffect(FFEffect):
       
   281     """
       
   282         video effect
       
   283     """
       
   284 
       
   285     def __init__(self, inputfile=None, **args):
       
   286         FFEffect.__init__(self, inputfile, **args)
       
   287 
       
   288     def bitrate(self, b=None):
       
   289         """ set video bitrate """
       
   290         if b:
       
   291             self.__effects__["b"] = "%sk" % int(b)
       
   292         return self.__effects__.get("b")
       
   293 
       
   294     def unset_bitrate(self):
       
   295         del self.__effects__["b"]
       
   296 
       
   297     def vframes(self, vframes=None):
       
   298         """ set number of video frames to record """
       
   299         if vframes:
       
   300             self.__effects__["vframes"] = int(vframes)
       
   301         return self.__effects__.get("vframes")
       
   302 
       
   303     def unset_vframes(self):
       
   304         del self.__effects__["vframes"]
       
   305 
       
   306     def rate(self, r=None):
       
   307         """ set frame rate """
       
   308         if r:
       
   309             self.__effects__["r"] = int(r)
       
   310         return self.__effects__.get("r")
       
   311 
       
   312     def unset_rate(self):
       
   313         del self.__effects__["r"]
       
   314 
       
   315     def size(self, s=None):
       
   316         """ set frame size """
       
   317         if s in ["sqcif", "qcif", "cif", "4cif", "qqvga", "qvga", "vga", "svga",
       
   318                  "xga", "uxga", "qxga", "sxga", "qsxga", "hsxga", "wvga", "wxga",
       
   319                  "wsxga", "wuxga", "wqxga", "wqsxga", "wquxga", "whsxga",
       
   320                  "whuxga", "cga", "ega", "hd480", "hd720", "hd1080"]:
       
   321             self.__effects__["s"] = s
       
   322         elif s:
       
   323             wh = s.split("x")
       
   324             if len(wh) == 2 and int(wh[0]) and int(wh[1]):
       
   325                 self.__effects__["s"] = s
       
   326             else:
       
   327                 raise FFException("Error parsing option: size")
       
   328         return self.__effects__.get("s")
       
   329 
       
   330     def unset_size(self):
       
   331         del  self.__effects__["s"]
       
   332 
       
   333     def aspect(self, aspect=None):
       
   334         """ set aspect ratio """
       
   335         if aspect:
       
   336             self.__effects__["aspect"] = aspect
       
   337         return self.__effects__.get("aspect")
       
   338 
       
   339     def unset_aspect(self):
       
   340         del self.__effects__["aspect"]
       
   341 
       
   342     def crop(self, top=0, bottom=0, left=0, right=0):
       
   343         """ set the crop size """
       
   344         if top % 2:
       
   345             top = top - 1
       
   346         if bottom % 2:
       
   347             bottom = bottom - 1
       
   348         if left % 2:
       
   349             left = left - 1
       
   350         if right % 2:
       
   351             right = right - 1
       
   352         if top:
       
   353             self.__effects__["croptop"] = top
       
   354         if bottom:
       
   355             self.__effects__["cropbottom"] = bottom
       
   356         if left:
       
   357             self.__effects__["cropleft"] = left
       
   358         if right:
       
   359             self.__effects__["cropright"] = right
       
   360         return self.__effects__.get("croptop"), self.__effects__.get("cropbottom"), self.__effects__.get("cropleft"), self.__effects__.get("cropright")
       
   361 
       
   362     def unset_crop(self):
       
   363         del self.__effects__["croptop"]
       
   364         del self.__effects__["cropbottom"]
       
   365         del self.__effects__["cropleft"]
       
   366         del self.__effects__["cropright"]
       
   367 
       
   368     def pad(self, top=0, bottom=0, left=0, right=0, color="000000"):
       
   369         """ set the pad band size and color as hex value """
       
   370         if top:
       
   371             self.__effects__["padtop"] = top
       
   372         if bottom:
       
   373             self.__effects__["padbottom"] = bottom
       
   374         if left:
       
   375             self.__effects__["padleft"] = left
       
   376         if right:
       
   377             self.__effects__["padright"] = right
       
   378         if color:
       
   379             self.__effects__["padcolor"] = color
       
   380         return self.__effects__.get("padtop"), self.__effects__.get("padbottom"), self.__effects__.get("padleft"), self.__effects__.get("padright"), self.__effects__.get("padcolor")
       
   381 
       
   382     def unset_pad(self):
       
   383         del self.__effects__["padtop"]
       
   384         del self.__effects__["padbottom"]
       
   385         del self.__effects__["padleft"]
       
   386         del self.__effects__["padright"]
       
   387 
       
   388     def vn(self):
       
   389         """ disable video recording """
       
   390         self.__effects__["vn"] = True
       
   391 
       
   392     def unset_vn(self):
       
   393         del self.__effects__["vn"]
       
   394 
       
   395     def bitratetolerance(self, bt=None):
       
   396         """ set bitrate tolerance """
       
   397         if bt:
       
   398             self.__effects__["bt"] = "%sk" % int(bt)
       
   399         return self.__effects__.get("bt")
       
   400 
       
   401     def unset_bitratetolerance(self):
       
   402         del self.__effects__["bt"]
       
   403 
       
   404     def bitraterange(self, minrate=None, maxrate=None):
       
   405         """ set min/max bitrate (bit/s) """
       
   406         if minrate or maxrate and not self.__effects__["bufsize"]:
       
   407             self.__effects__["bufsize"] = 4096
       
   408         if minrate:
       
   409             self.__effects__["minrate"] = minrate
       
   410         if maxrate:
       
   411             self.__effects__["maxrate"] = maxrate
       
   412 
       
   413         return self.__effects__.get("minrate"), self.__effects__.get("maxrate")
       
   414 
       
   415     def unset_bitraterange(self):
       
   416         del self.__effects__["maxrate"]
       
   417         del self.__effects__["minrate"]
       
   418 
       
   419     def bufsize(self, bufsize=4096):
       
   420         """ set buffer size (bits) """
       
   421         self.__effects__["bufsize"] = int(bufsize)
       
   422         return self.__effects__["bufsize"]
       
   423 
       
   424     def unset_bufsize(self):
       
   425         del self.__effects__["bufsize"]
       
   426 
       
   427     def vcodec(self, vcodec="copy"):
       
   428         """ set video codec """
       
   429         self.__effects__["vcodec"] = vcodec
       
   430         return self.__effects__["vcodec"]
       
   431 
       
   432     def unset_vcodec(self):
       
   433         del self.__effects__["vcodec"]
       
   434 
       
   435     def sameq(self):
       
   436         """ use same video quality as source """
       
   437         self.__effects__["sameq"] = True
       
   438 
       
   439     def unset_sameq(self):
       
   440         del self.__effects__["sameq"]
       
   441 
       
   442     def passenc(self, p=1):
       
   443         """ select pass number (1 or 2)"""
       
   444         self.__effects__["pass"] = (int(p) % 3 + 1) % 2 + 1 #!!!
       
   445         return self.__effects__["pass"]
       
   446 
       
   447     def unset_passenc(self):
       
   448         del self.__effects__["pass"]
       
   449 
       
   450     def pixelformat(self, p=None):
       
   451         """ set pixelformat """
       
   452         if p:
       
   453             self.__effects__["pix_fmt"] = p
       
   454         return self.__effects__.get("pix_fmt")
       
   455 
       
   456     def unset_pixelformat(self):
       
   457         del self.__effects__["pix_fmt"]
       
   458 
       
   459     #TODO: sws_flag
       
   460 
       
   461     def picturesize(self, gop=None):
       
   462         """ set of group pictures size """
       
   463         if gop:
       
   464             self.__effects__["gop"] = int(gop)
       
   465         return self.__effects__.get("gop")
       
   466 
       
   467     def unset_picturesize(self):
       
   468         del self.__effects__["gop"]
       
   469 
       
   470     def intra(self):
       
   471         """ use only intra frames """
       
   472         self.__effects__["intra"] = True
       
   473 
       
   474     def unset_intra(self):
       
   475         del self.__effects__["intra"]
       
   476 
       
   477     def vdthreshold(self, vdt=None):
       
   478         """ discard threshold """
       
   479         if vdt:
       
   480             self.__effects__["vdt"] = int(vdt)
       
   481         return self.__effects__.get("vdt")
       
   482 
       
   483     def unset_vdthreshold(self):
       
   484         del self.__effects__["vdt"]
       
   485 
       
   486     def quantizerscale(self, qscale=None):
       
   487         """ Fixed quantizer scale """
       
   488         if qscale:
       
   489             self.__effects__["qscale"] = int(qscale)
       
   490         return self.__effects__.get("qscale")
       
   491 
       
   492     def unset_quantizerscale(self):
       
   493         del self.__effects__["qscale"]
       
   494 
       
   495     def quantizerrange(self, qmin=None, qmax=None, qdiff=None):
       
   496         """ define min/max quantizer scale """
       
   497         if qdiff:
       
   498             self.__effects__["qdiff"] = int(qdiff)
       
   499         else:
       
   500             if qmin:
       
   501                 self.__effects__["qmin"] = int(qmin)
       
   502             if qmax:
       
   503                 self.__effects__["qmax"] = int(qmax)
       
   504         return self.__effects__.get("qmin"), self.__effects__.get("qmax"), self.__effects__.get("qdiff"),
       
   505 
       
   506     def unset_quantizerrange(self):
       
   507         del self.__effects__["qdiff"]
       
   508 
       
   509     def quantizerblur(self, qblur=None):
       
   510         """ video quantizer scale blur """
       
   511         if qblur:
       
   512             self.__effects__["qblur"] = float(qblur)
       
   513         return self.__effects__.get("qblur")
       
   514 
       
   515     def unset_quantizerblur(self):
       
   516         del self.__effects__["qblur"]
       
   517 
       
   518     def quantizercompression(self, qcomp=0.5):
       
   519         """ video quantizer scale compression """
       
   520         self.__effects__["qcomp"] = float(qcomp)
       
   521         return self.__effects__["qcomp"]
       
   522 
       
   523     def unset_quantizercompression(self):
       
   524         del self.__effects__["qcomp"]
       
   525 
       
   526     def lagrangefactor(self, lmin=None, lmax=None):
       
   527         """ min/max lagrange factor """
       
   528         if lmin:
       
   529             self.__effects__["lmin"] = int(lmin)
       
   530         if lmax:
       
   531             self.__effects__["lmax"] = int(lmax)
       
   532         return self.__effects__.get("lmin"), self.__effects__.get("lmax")
       
   533 
       
   534     def unset_lagrangefactor(self):
       
   535         del self.__effects__["lmin"]
       
   536         del self.__effects__["lmax"]
       
   537 
       
   538     def macroblock(self, mblmin=None, mblmax=None):
       
   539         """ min/max macroblock scale """
       
   540         if mblmin:
       
   541             self.__effects__["mblmin"] = int(mblmin)
       
   542         if mblmax:
       
   543             self.__effects__["mblmax"] = int(mblmax)
       
   544         return self.__effects__.get("mblmin"), self.__effects__.get("mblmax")
       
   545 
       
   546     def unset_macroblock(self):
       
   547         del self.__effects__["mblmin"]
       
   548         del self.__effects__["mblmax"]
       
   549 
       
   550     #TODO: read man pages !
       
   551 
       
   552 
       
   553 class FFAudioEffect(FFEffect):
       
   554     """
       
   555         Audio effect
       
   556     """
       
   557 
       
   558     def __init__(self, inputfile, **args):
       
   559         FFEffect.__init__(self, inputfile, **args)
       
   560 
       
   561     def aframes(self, aframes=None):
       
   562         """ set number of audio frames to record """
       
   563         if aframes:
       
   564             self.__effects__["aframes"] = int(aframes)
       
   565         return self.__effects__.get("aframes")
       
   566 
       
   567     def unset_aframes(self):
       
   568         del self.__effects__["aframes"]
       
   569 
       
   570     def audiosampling(self, ar=44100):
       
   571         """ set audio sampling frequency (Hz)"""
       
   572         self.__effects__["ar"] = int(ar)
       
   573         return self.__effects__["ar"]
       
   574 
       
   575     def unset_audiosampling(self):
       
   576         del self.__effects__["ar"]
       
   577 
       
   578     def audiobitrate(self, ab=64):
       
   579         """ set audio bitrate (kbit/s)"""
       
   580         self.__effects__["ab"] = int(ab)
       
   581         return self.__effects__["ab"]
       
   582 
       
   583     def unset_audiobitrate(self):
       
   584         del self.__effects__["ab"]
       
   585 
       
   586     def audiochannels(self, ac=1):
       
   587         """ set number of audio channels """
       
   588         self.__effects__["ac"] = int(ac)
       
   589         return self.__effects__["ac"]
       
   590 
       
   591     def unset_audiochannels(self):
       
   592         del self.__effects__["ac"]
       
   593 
       
   594     def audiorecording(self):
       
   595         """ disable audio recording """
       
   596         self.__effects__["an"] = True
       
   597 
       
   598     def unset_audiorecording(self):
       
   599         del self.__effects__["an"]
       
   600 
       
   601     def acodec(self, acodec="copy"):
       
   602         """ select audio codec """
       
   603         self.__effects__["acodec"] = acodec
       
   604         return self.__effects__["acodec"]
       
   605 
       
   606     def unset_acodec(self):
       
   607         del self.__effects__["acodec"]
       
   608 
       
   609     def newaudio(self):
       
   610         """ add new audio track """
       
   611         self.__effects__["newaudio"] = True
       
   612 
       
   613     def unset_newaudio(self):
       
   614         del self.__effects__["newaudio"]