Ejemplo n.º 1
0
    def get_size_ffmpeg(self, ffmpeg_path, fname):
        cmd = [ffmpeg_path, '-i', fname]
        # Windows and other OS buffer 4096 and ffmpeg can output more
        # than that.
        err_tmp = tempfile.TemporaryFile()
        ffmpeg = subprocess.Popen(cmd, stderr=err_tmp,
                                  stdout=subprocess.PIPE,
                                  stdin=subprocess.PIPE)

        # wait configured # of seconds: if ffmpeg is not back give up
        limit = config.getFFmpegWait()
        if limit:
            for i in xrange(limit * 20):
                time.sleep(.05)
                if not ffmpeg.poll() == None:
                    break

            if ffmpeg.poll() == None:
                kill(ffmpeg)
                return False, 'FFmpeg timed out'
        else:
            ffmpeg.wait()

        err_tmp.seek(0)
        output = err_tmp.read()
        err_tmp.close()

        x = ffmpeg_size.search(output)
        if x:
            width = int(x.group(1))
            height = int(x.group(2))
        else:
            return False, "Couldn't parse size"

        return True, (width, height)
Ejemplo n.º 2
0
    def get_size_ffmpeg(self, ffmpeg_path, fname):
        cmd = [ffmpeg_path, '-i', fname]
        # Windows and other OS buffer 4096 and ffmpeg can output more
        # than that.
        err_tmp = tempfile.TemporaryFile()
        ffmpeg = subprocess.Popen(cmd, stderr=err_tmp,
                                  stdout=subprocess.PIPE,
                                  stdin=subprocess.PIPE)

        # wait configured # of seconds: if ffmpeg is not back give up
        limit = config.getFFmpegWait()
        if limit:
            for i in xrange(limit * 20):
                time.sleep(.05)
                if not ffmpeg.poll() == None:
                    break

            if ffmpeg.poll() == None:
                kill(ffmpeg)
                return False, 'FFmpeg timed out'
        else:
            ffmpeg.wait()

        err_tmp.seek(0)
        output = err_tmp.read()
        err_tmp.close()

        x = ffmpeg_size.search(output)
        if x:
            width = int(x.group(1))
            height = int(x.group(2))
        else:
            return False, "Couldn't parse size"

        return True, (width, height)
Ejemplo n.º 3
0
def detect_h264_level(fname, vstream=0):
    ffprobe_path = config.get_bin('ffprobe')
    if not ffprobe_path:
        return 0

    cmd = [ffprobe_path, '-of', 'flat', '-show_streams', '-i', fname]
    # Windows and other OS buffer 4096 and ffprobe can output more than that.
    out_tmp = tempfile.TemporaryFile()
    ffprobe = subprocess.Popen(cmd, stdout=out_tmp, stderr=subprocess.PIPE,
                               stdin=subprocess.PIPE)

    # wait configured # of seconds: if ffprobe is not back give up
    limit = config.getFFmpegWait()
    if limit:
        for i in xrange(limit * 20):
            time.sleep(.05)
            if not ffprobe.poll() == None:
                break

        if ffprobe.poll() == None:
            kill(ffprobe)
            return 0
    else:
        ffprobe.wait()

    out_tmp.seek(0)
    output = out_tmp.read()
    out_tmp.close()
    debug('ffprobe output=%s' % repr(output))

    match = re.search(r'streams\.stream\.\d+\.level=(\d+)', output)
    if match:
        return int(match.group(1))
    return 0
Ejemplo n.º 4
0
def detect_h264_level(fname, vstream=0):
    ffprobe_path = config.get_bin('ffprobe')
    if not ffprobe_path:
        return 0

    cmd = [ffprobe_path, '-of', 'flat', '-show_streams', '-i', fname]
    # Windows and other OS buffer 4096 and ffprobe can output more than that.
    out_tmp = tempfile.TemporaryFile()
    ffprobe = subprocess.Popen(cmd,
                               stdout=out_tmp,
                               stderr=subprocess.PIPE,
                               stdin=subprocess.PIPE)

    # wait configured # of seconds: if ffprobe is not back give up
    limit = config.getFFmpegWait()
    if limit:
        for i in xrange(limit * 20):
            time.sleep(.05)
            if not ffprobe.poll() == None:
                break

        if ffprobe.poll() == None:
            kill(ffprobe)
            return 0
    else:
        ffprobe.wait()

    out_tmp.seek(0)
    output = out_tmp.read()
    out_tmp.close()
    debug('ffprobe output=%s' % repr(output))

    match = re.search(r'streams\.stream\.\d+\.level=(\d+)', output)
    if match:
        return int(match.group(1))
    return 0
Ejemplo n.º 5
0
def video_info(inFile, cache=True):
    vInfo = dict()
    fname = unicode(inFile, 'utf-8')
    mtime = os.stat(fname).st_mtime
    if cache:
        if inFile in info_cache and info_cache[inFile][0] == mtime:
            debug('CACHE HIT! %s' % inFile)
            return info_cache[inFile][1]

    vInfo['Supported'] = True

    ffmpeg_path = config.get_bin('ffmpeg')
    if not ffmpeg_path:
        if os.path.splitext(inFile)[1].lower() not in ['.mpg', '.mpeg',
                                                       '.vob', '.tivo']:
            vInfo['Supported'] = False
        vInfo.update({'millisecs': 0, 'vWidth': 704, 'vHeight': 480,
                      'rawmeta': {}})
        if cache:
            info_cache[inFile] = (mtime, vInfo)
        return vInfo

    if mswindows:
        fname = fname.encode('iso8859-1')
    cmd = [ffmpeg_path, '-i', fname]
    # Windows and other OS buffer 4096 and ffmpeg can output more than that.
    err_tmp = tempfile.TemporaryFile()
    ffmpeg = subprocess.Popen(cmd, stderr=err_tmp, stdout=subprocess.PIPE,
                              stdin=subprocess.PIPE)

    # wait configured # of seconds: if ffmpeg is not back give up
    limit = config.getFFmpegWait()
    if limit:
        for i in xrange(limit * 20):
            time.sleep(.05)
            if not ffmpeg.poll() == None:
                break

        if ffmpeg.poll() == None:
            kill(ffmpeg)
            vInfo['Supported'] = False
            if cache:
                info_cache[inFile] = (mtime, vInfo)
            return vInfo
    else:
        ffmpeg.wait()

    err_tmp.seek(0)
    output = err_tmp.read()
    err_tmp.close()
    debug('ffmpeg output=%s' % output)

    attrs = {'container': r'Input #0, ([^,]+),',
             'vCodec': r'Video: ([^, ]+)',             # video codec
             'aKbps': r'.*Audio: .+, (.+) (?:kb/s).*',     # audio bitrate
             'aCodec': r'.*Audio: ([^, ]+)',             # audio codec
             'aFreq': r'.*Audio: .+, (.+) (?:Hz).*',       # audio frequency
             'mapVideo': r'([0-9]+[.:]+[0-9]+).*: Video:.*'}  # video mapping

    for attr in attrs:
        rezre = re.compile(attrs[attr])
        x = rezre.search(output)
        if x:
            vInfo[attr] = x.group(1)
        else:
            if attr in ['container', 'vCodec']:
                vInfo[attr] = ''
                vInfo['Supported'] = False
            else:
                vInfo[attr] = None
            debug('failed at ' + attr)

    rezre = re.compile(r'.*Audio: .+, (?:(\d+)(?:(?:\.(\d).*)?(?: channels.*)?)|(stereo|mono)),.*')
    x = rezre.search(output)
    if x:
        if x.group(3):
            if x.group(3) == 'stereo':
                vInfo['aCh'] = 2
            elif x.group(3) == 'mono':
                vInfo['aCh'] = 1
        elif x.group(2):
            vInfo['aCh'] = int(x.group(1)) + int(x.group(2))
        elif x.group(1):
            vInfo['aCh'] = int(x.group(1))
        else:
            vInfo['aCh'] = None
            debug('failed at aCh')
    else:
        vInfo['aCh'] = None
        debug('failed at aCh')

    rezre = re.compile(r'.*Video: .+, (\d+)x(\d+)[, ].*')
    x = rezre.search(output)
    if x:
        vInfo['vWidth'] = int(x.group(1))
        vInfo['vHeight'] = int(x.group(2))
    else:
        vInfo['vWidth'] = ''
        vInfo['vHeight'] = ''
        vInfo['Supported'] = False
        debug('failed at vWidth/vHeight')

    rezre = re.compile(r'.*Video: .+, (.+) (?:fps|tb\(r\)|tbr).*')
    x = rezre.search(output)
    if x:
        vInfo['vFps'] = x.group(1)
        if '.' not in vInfo['vFps']:
            vInfo['vFps'] += '.00'

        # Allow override only if it is mpeg2 and frame rate was doubled
        # to 59.94

        if vInfo['vCodec'] == 'mpeg2video' and vInfo['vFps'] != '29.97':
            # First look for the build 7215 version
            rezre = re.compile(r'.*film source: 29.97.*')
            x = rezre.search(output.lower())
            if x:
                debug('film source: 29.97 setting vFps to 29.97')
                vInfo['vFps'] = '29.97'
            else:
                # for build 8047:
                rezre = re.compile(r'.*frame rate differs from container ' +
                                   r'frame rate: 29.97.*')
                debug('Bug in VideoReDo')
                x = rezre.search(output.lower())
                if x:
                    vInfo['vFps'] = '29.97'
    else:
        vInfo['vFps'] = ''
        vInfo['Supported'] = False
        debug('failed at vFps')

    durre = re.compile(r'.*Duration: ([0-9]+):([0-9]+):([0-9]+)\.([0-9]+),')
    d = durre.search(output)

    if d:
        vInfo['millisecs'] = ((int(d.group(1)) * 3600 +
                               int(d.group(2)) * 60 +
                               int(d.group(3))) * 1000 +
                              int(d.group(4)) * (10 ** (3 - len(d.group(4)))))
    else:
        vInfo['millisecs'] = 0

    # get bitrate of source for tivo compatibility test.
    rezre = re.compile(r'.*bitrate: (.+) (?:kb/s).*')
    x = rezre.search(output)
    if x:
        vInfo['kbps'] = x.group(1)
    else:
        # Fallback method of getting video bitrate
        # Sample line:  Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p,
        #               720x480 [PAR 32:27 DAR 16:9], 9800 kb/s, 59.94 tb(r)
        rezre = re.compile(r'.*Stream #0\.0\[.*\]: Video: mpeg2video, ' +
                           r'\S+, \S+ \[.*\], (\d+) (?:kb/s).*')
        x = rezre.search(output)
        if x:
            vInfo['kbps'] = x.group(1)
        else:
            vInfo['kbps'] = None
            debug('failed at kbps')

    # get par.
    rezre = re.compile(r'.*Video: .+PAR ([0-9]+):([0-9]+) DAR [0-9:]+.*')
    x = rezre.search(output)
    if x and x.group(1) != "0" and x.group(2) != "0":
        vInfo['par1'] = x.group(1) + ':' + x.group(2)
        vInfo['par2'] = float(x.group(1)) / float(x.group(2))
    else:
        vInfo['par1'], vInfo['par2'] = None, None

    # get dar.
    rezre = re.compile(r'.*Video: .+DAR ([0-9]+):([0-9]+).*')
    x = rezre.search(output)
    if x and x.group(1) != "0" and x.group(2) != "0":
        vInfo['dar1'] = x.group(1) + ':' + x.group(2)
    else:
        vInfo['dar1'] = None

    # get Audio Stream mapping.
    rezre = re.compile(r'([0-9]+[.:]+[0-9]+)(.*): Audio:(.*)')
    x = rezre.search(output)
    amap = []
    if x:
        for x in rezre.finditer(output):
            amap.append((x.group(1), x.group(2) + x.group(3)))
    else:
        amap.append(('', ''))
        debug('failed at mapAudio')
    vInfo['mapAudio'] = amap

    vInfo['par'] = None

    # get Metadata dump (newer ffmpeg).
    lines = output.split('\n')
    rawmeta = {}
    flag = False

    for line in lines:
        if line.startswith('  Metadata:'):
            flag = True
        else:
            if flag:
                if line.startswith('  Duration:'):
                    flag = False
                else:
                    try:
                        key, value = [x.strip() for x in line.split(':', 1)]
                        try:
                            value = value.decode('utf-8')
                        except:
                            if sys.platform == 'darwin':
                                value = value.decode('macroman')
                            else:
                                value = value.decode('iso8859-1')
                        rawmeta[key] = [value]
                    except:
                        pass

    vInfo['rawmeta'] = rawmeta

    data = metadata.from_text(inFile)
    for key in data:
        if key.startswith('Override_'):
            vInfo['Supported'] = True
            if key.startswith('Override_mapAudio'):
                audiomap = dict(vInfo['mapAudio'])
                newmap = shlex.split(data[key])
                audiomap.update(zip(newmap[::2], newmap[1::2]))
                vInfo['mapAudio'] = sorted(audiomap.items(),
                                           key=lambda (k,v): (k,v))
            elif key.startswith('Override_millisecs'):
                vInfo[key.replace('Override_', '')] = int(data[key])
            else:
                vInfo[key.replace('Override_', '')] = data[key]

    if cache:
        info_cache[inFile] = (mtime, vInfo)
    debug("; ".join(["%s=%s" % (k, v) for k, v in vInfo.items()]))
    return vInfo
Ejemplo n.º 6
0
    def get_image_ffmpeg(self, path, width, height, pshape, rot, attrs):
        ffmpeg_path = config.get_bin('ffmpeg')
        if not ffmpeg_path:
            return False, 'FFmpeg not found'

        fname = unicode(path, 'utf-8')
        if sys.platform == 'win32':
            fname = fname.encode('iso8859-1')

        if attrs and 'size' in attrs:
            result = attrs['size']
        else:
            status, result = self.get_size_ffmpeg(ffmpeg_path, fname)
            if not status:
                return False, result
            if attrs:
                attrs['size'] = result

        if rot in (90, 270):
            oldh, oldw = result
        else:
            oldw, oldh = result

        width, height = self.new_size(oldw, oldh, width, height, pshape)

        if rot == 270:
            filters = 'transpose=1,'
        elif rot == 180:
            filters = 'hflip,vflip,'
        elif rot == 90:
            filters = 'transpose=2,'
        else:
            filters = ''

        filters += 'format=yuvj420p,'

        neww, newh = oldw, oldh
        while (neww / width >= 50) or (newh / height >= 50):
            neww /= 2
            newh /= 2
            filters += 'scale=%d:%d,' % (neww, newh)

        filters += 'scale=%d:%d' % (width, height)

        cmd = [ffmpeg_path, '-i', fname, '-vf', filters, '-f', 'mjpeg', '-']
        jpeg_tmp = tempfile.TemporaryFile()
        ffmpeg = subprocess.Popen(cmd, stdout=jpeg_tmp,
                                  stdin=subprocess.PIPE)

        # wait configured # of seconds: if ffmpeg is not back give up
        limit = config.getFFmpegWait()
        if limit:
            for i in xrange(limit * 20):
                time.sleep(.05)
                if not ffmpeg.poll() == None:
                    break

            if ffmpeg.poll() == None:
                kill(ffmpeg)
                return False, 'FFmpeg timed out'
        else:
            ffmpeg.wait()

        jpeg_tmp.seek(0)
        output = jpeg_tmp.read()
        jpeg_tmp.close()

        if 'JFIF' not in output[:10]:
            output = output[:2] + JFIF_TAG + output[2:]

        return True, output
Ejemplo n.º 7
0
def video_info(inFile, cache=True):
    vInfo = dict()
    fname = unicode(inFile, "utf-8")
    mtime = os.path.getmtime(fname)
    if cache:
        if inFile in info_cache and info_cache[inFile][0] == mtime:
            debug("CACHE HIT! %s" % inFile)
            return info_cache[inFile][1]

    vInfo["Supported"] = True

    ffmpeg_path = config.get_bin("ffmpeg")
    if not ffmpeg_path:
        if os.path.splitext(inFile)[1].lower() not in [".mpg", ".mpeg", ".vob", ".tivo", ".ts"]:
            vInfo["Supported"] = False
        vInfo.update({"millisecs": 0, "vWidth": 704, "vHeight": 480, "rawmeta": {}})
        if cache:
            info_cache[inFile] = (mtime, vInfo)
        return vInfo

    if mswindows:
        fname = fname.encode("cp1252")
    cmd = [ffmpeg_path, "-i", fname]
    # Windows and other OS buffer 4096 and ffmpeg can output more than that.
    err_tmp = tempfile.TemporaryFile()
    ffmpeg = subprocess.Popen(cmd, stderr=err_tmp, stdout=subprocess.PIPE, stdin=subprocess.PIPE)

    # wait configured # of seconds: if ffmpeg is not back give up
    limit = config.getFFmpegWait()
    if limit:
        for i in xrange(limit * 20):
            time.sleep(0.05)
            if not ffmpeg.poll() == None:
                break

        if ffmpeg.poll() == None:
            kill(ffmpeg)
            vInfo["Supported"] = False
            if cache:
                info_cache[inFile] = (mtime, vInfo)
            return vInfo
    else:
        ffmpeg.wait()

    err_tmp.seek(0)
    output = err_tmp.read()
    err_tmp.close()
    debug("ffmpeg output=%s" % output)

    attrs = {
        "container": r"Input #0, ([^,]+),",
        "vCodec": r"Video: ([^, ]+)",  # video codec
        "aKbps": r".*Audio: .+, (.+) (?:kb/s).*",  # audio bitrate
        "aCodec": r".*Audio: ([^, ]+)",  # audio codec
        "aFreq": r".*Audio: .+, (.+) (?:Hz).*",  # audio frequency
        "mapVideo": r"([0-9]+[.:]+[0-9]+).*: Video:.*",
    }  # video mapping

    for attr in attrs:
        rezre = re.compile(attrs[attr])
        x = rezre.search(output)
        if x:
            vInfo[attr] = x.group(1)
        else:
            if attr in ["container", "vCodec"]:
                vInfo[attr] = ""
                vInfo["Supported"] = False
            else:
                vInfo[attr] = None
            debug("failed at " + attr)

    rezre = re.compile(r".*Audio: .+, (?:(\d+)(?:(?:\.(\d).*)?(?: channels.*)?)|(stereo|mono)),.*")
    x = rezre.search(output)
    if x:
        if x.group(3):
            if x.group(3) == "stereo":
                vInfo["aCh"] = 2
            elif x.group(3) == "mono":
                vInfo["aCh"] = 1
        elif x.group(2):
            vInfo["aCh"] = int(x.group(1)) + int(x.group(2))
        elif x.group(1):
            vInfo["aCh"] = int(x.group(1))
        else:
            vInfo["aCh"] = None
            debug("failed at aCh")
    else:
        vInfo["aCh"] = None
        debug("failed at aCh")

    rezre = re.compile(r".*Video: .+, (\d+)x(\d+)[, ].*")
    x = rezre.search(output)
    if x:
        vInfo["vWidth"] = int(x.group(1))
        vInfo["vHeight"] = int(x.group(2))
    else:
        vInfo["vWidth"] = ""
        vInfo["vHeight"] = ""
        vInfo["Supported"] = False
        debug("failed at vWidth/vHeight")

    rezre = re.compile(r".*Video: .+, (.+) (?:fps|tb\(r\)|tbr).*")
    x = rezre.search(output)
    if x:
        vInfo["vFps"] = x.group(1)
        if "." not in vInfo["vFps"]:
            vInfo["vFps"] += ".00"

        # Allow override only if it is mpeg2 and frame rate was doubled
        # to 59.94

        if vInfo["vCodec"] == "mpeg2video" and vInfo["vFps"] != "29.97":
            # First look for the build 7215 version
            rezre = re.compile(r".*film source: 29.97.*")
            x = rezre.search(output.lower())
            if x:
                debug("film source: 29.97 setting vFps to 29.97")
                vInfo["vFps"] = "29.97"
            else:
                # for build 8047:
                rezre = re.compile(r".*frame rate differs from container " + r"frame rate: 29.97.*")
                debug("Bug in VideoReDo")
                x = rezre.search(output.lower())
                if x:
                    vInfo["vFps"] = "29.97"
    else:
        vInfo["vFps"] = ""
        vInfo["Supported"] = False
        debug("failed at vFps")

    durre = re.compile(r".*Duration: ([0-9]+):([0-9]+):([0-9]+)\.([0-9]+),")
    d = durre.search(output)

    if d:
        vInfo["millisecs"] = (int(d.group(1)) * 3600 + int(d.group(2)) * 60 + int(d.group(3))) * 1000 + int(
            d.group(4)
        ) * (10 ** (3 - len(d.group(4))))
    else:
        vInfo["millisecs"] = 0

    # get bitrate of source for tivo compatibility test.
    rezre = re.compile(r".*bitrate: (.+) (?:kb/s).*")
    x = rezre.search(output)
    if x:
        vInfo["kbps"] = x.group(1)
    else:
        # Fallback method of getting video bitrate
        # Sample line:  Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p,
        #               720x480 [PAR 32:27 DAR 16:9], 9800 kb/s, 59.94 tb(r)
        rezre = re.compile(r".*Stream #0\.0\[.*\]: Video: mpeg2video, " + r"\S+, \S+ \[.*\], (\d+) (?:kb/s).*")
        x = rezre.search(output)
        if x:
            vInfo["kbps"] = x.group(1)
        else:
            vInfo["kbps"] = None
            debug("failed at kbps")

    # get par.
    rezre = re.compile(r".*Video: .+PAR ([0-9]+):([0-9]+) DAR [0-9:]+.*")
    x = rezre.search(output)
    if x and x.group(1) != "0" and x.group(2) != "0":
        vInfo["par1"] = x.group(1) + ":" + x.group(2)
        vInfo["par2"] = float(x.group(1)) / float(x.group(2))
    else:
        vInfo["par1"], vInfo["par2"] = None, None

    # get dar.
    rezre = re.compile(r".*Video: .+DAR ([0-9]+):([0-9]+).*")
    x = rezre.search(output)
    if x and x.group(1) != "0" and x.group(2) != "0":
        vInfo["dar1"] = x.group(1) + ":" + x.group(2)
    else:
        vInfo["dar1"] = None

    # get Audio Stream mapping.
    rezre = re.compile(r"([0-9]+[.:]+[0-9]+)(.*): Audio:(.*)")
    x = rezre.search(output)
    amap = []
    if x:
        for x in rezre.finditer(output):
            amap.append((x.group(1), x.group(2) + x.group(3)))
    else:
        amap.append(("", ""))
        debug("failed at mapAudio")
    vInfo["mapAudio"] = amap

    vInfo["par"] = None

    # get Metadata dump (newer ffmpeg).
    lines = output.split("\n")
    rawmeta = {}
    flag = False

    for line in lines:
        if line.startswith("  Metadata:"):
            flag = True
        else:
            if flag:
                if line.startswith("  Duration:"):
                    flag = False
                else:
                    try:
                        key, value = [x.strip() for x in line.split(":", 1)]
                        try:
                            value = value.decode("utf-8")
                        except:
                            if sys.platform == "darwin":
                                value = value.decode("macroman")
                            else:
                                value = value.decode("cp1252")
                        rawmeta[key] = [value]
                    except:
                        pass

    vInfo["rawmeta"] = rawmeta

    data = metadata.from_text(inFile)
    for key in data:
        if key.startswith("Override_"):
            vInfo["Supported"] = True
            if key.startswith("Override_mapAudio"):
                audiomap = dict(vInfo["mapAudio"])
                newmap = shlex.split(data[key])
                audiomap.update(zip(newmap[::2], newmap[1::2]))
                vInfo["mapAudio"] = sorted(audiomap.items(), key=lambda (k, v): (k, v))
            elif key.startswith("Override_millisecs"):
                vInfo[key.replace("Override_", "")] = int(data[key])
            else:
                vInfo[key.replace("Override_", "")] = data[key]

    if cache:
        info_cache[inFile] = (mtime, vInfo)
    debug("; ".join(["%s=%s" % (k, v) for k, v in vInfo.items()]))
    return vInfo
Ejemplo n.º 8
0
def video_info(inFile, cache=True):
    vInfo = dict()
    fname = unicode(inFile, 'utf-8')
    mtime = os.path.getmtime(fname)
    if cache:
        if inFile in info_cache and info_cache[inFile][0] == mtime:
            debug('CACHE HIT! %s' % inFile)
            return info_cache[inFile][1]

    vInfo['Supported'] = True

    ffmpeg_path = config.get_bin('ffmpeg')
    if not ffmpeg_path:
        if os.path.splitext(inFile)[1].lower() not in [
                '.mpg', '.mpeg', '.vob', '.tivo', '.ts'
        ]:
            vInfo['Supported'] = False
        vInfo.update({
            'millisecs': 0,
            'vWidth': 704,
            'vHeight': 480,
            'rawmeta': {}
        })
        if cache:
            info_cache[inFile] = (mtime, vInfo)
        return vInfo

    if mswindows:
        fname = fname.encode('cp1252')
    cmd = [ffmpeg_path, '-i', fname]
    # Windows and other OS buffer 4096 and ffmpeg can output more than that.
    err_tmp = tempfile.TemporaryFile()
    ffmpeg = subprocess.Popen(cmd,
                              stderr=err_tmp,
                              stdout=subprocess.PIPE,
                              stdin=subprocess.PIPE)

    # wait configured # of seconds: if ffmpeg is not back give up
    limit = config.getFFmpegWait()
    if limit:
        for i in xrange(limit * 20):
            time.sleep(.05)
            if not ffmpeg.poll() == None:
                break

        if ffmpeg.poll() == None:
            kill(ffmpeg)
            vInfo['Supported'] = False
            if cache:
                info_cache[inFile] = (mtime, vInfo)
            return vInfo
    else:
        ffmpeg.wait()

    err_tmp.seek(0)
    output = err_tmp.read()
    err_tmp.close()
    debug('ffmpeg output=%s' % output)

    attrs = {
        'container': r'Input #0, ([^,]+),',
        'vCodec': r'Video: ([^, ]+)',  # video codec
        'aKbps': r'.*Audio: .+, (.+) (?:kb/s).*',  # audio bitrate
        'aCodec': r'.*Audio: ([^, ]+)',  # audio codec
        'aFreq': r'.*Audio: .+, (.+) (?:Hz).*',  # audio frequency
        'mapVideo': r'([0-9]+[.:]+[0-9]+).*: Video:.*'
    }  # video mapping

    for attr in attrs:
        rezre = re.compile(attrs[attr])
        x = rezre.search(output)
        if x:
            vInfo[attr] = x.group(1)
        else:
            if attr in ['container', 'vCodec']:
                vInfo[attr] = ''
                vInfo['Supported'] = False
            else:
                vInfo[attr] = None
            debug('failed at ' + attr)

    rezre = re.compile(
        r'.*Audio: .+, (?:(\d+)(?:(?:\.(\d).*)?(?: channels.*)?)|(stereo|mono)),.*'
    )
    x = rezre.search(output)
    if x:
        if x.group(3):
            if x.group(3) == 'stereo':
                vInfo['aCh'] = 2
            elif x.group(3) == 'mono':
                vInfo['aCh'] = 1
        elif x.group(2):
            vInfo['aCh'] = int(x.group(1)) + int(x.group(2))
        elif x.group(1):
            vInfo['aCh'] = int(x.group(1))
        else:
            vInfo['aCh'] = None
            debug('failed at aCh')
    else:
        vInfo['aCh'] = None
        debug('failed at aCh')

    rezre = re.compile(r'.*Video: .+, (\d+)x(\d+)[, ].*')
    x = rezre.search(output)
    if x:
        vInfo['vWidth'] = int(x.group(1))
        vInfo['vHeight'] = int(x.group(2))
    else:
        vInfo['vWidth'] = ''
        vInfo['vHeight'] = ''
        vInfo['Supported'] = False
        debug('failed at vWidth/vHeight')

    rezre = re.compile(r'.*Video: .+, (.+) (?:fps|tb\(r\)|tbr).*')
    x = rezre.search(output)
    if x:
        vInfo['vFps'] = x.group(1)
        if '.' not in vInfo['vFps']:
            vInfo['vFps'] += '.00'

        # Allow override only if it is mpeg2 and frame rate was doubled
        # to 59.94

        if vInfo['vCodec'] == 'mpeg2video' and vInfo['vFps'] != '29.97':
            # First look for the build 7215 version
            rezre = re.compile(r'.*film source: 29.97.*')
            x = rezre.search(output.lower())
            if x:
                debug('film source: 29.97 setting vFps to 29.97')
                vInfo['vFps'] = '29.97'
            else:
                # for build 8047:
                rezre = re.compile(r'.*frame rate differs from container ' +
                                   r'frame rate: 29.97.*')
                debug('Bug in VideoReDo')
                x = rezre.search(output.lower())
                if x:
                    vInfo['vFps'] = '29.97'
    else:
        vInfo['vFps'] = ''
        vInfo['Supported'] = False
        debug('failed at vFps')

    durre = re.compile(r'.*Duration: ([0-9]+):([0-9]+):([0-9]+)\.([0-9]+),')
    d = durre.search(output)

    if d:
        vInfo['millisecs'] = (
            (int(d.group(1)) * 3600 + int(d.group(2)) * 60 + int(d.group(3))) *
            1000 + int(d.group(4)) * (10**(3 - len(d.group(4)))))
    else:
        vInfo['millisecs'] = 0

    # get bitrate of source for tivo compatibility test.
    rezre = re.compile(r'.*bitrate: (.+) (?:kb/s).*')
    x = rezre.search(output)
    if x:
        vInfo['kbps'] = x.group(1)
    else:
        # Fallback method of getting video bitrate
        # Sample line:  Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p,
        #               720x480 [PAR 32:27 DAR 16:9], 9800 kb/s, 59.94 tb(r)
        rezre = re.compile(r'.*Stream #0\.0\[.*\]: Video: mpeg2video, ' +
                           r'\S+, \S+ \[.*\], (\d+) (?:kb/s).*')
        x = rezre.search(output)
        if x:
            vInfo['kbps'] = x.group(1)
        else:
            vInfo['kbps'] = None
            debug('failed at kbps')

    # get par.
    rezre = re.compile(r'.*Video: .+PAR ([0-9]+):([0-9]+) DAR [0-9:]+.*')
    x = rezre.search(output)
    if x and x.group(1) != "0" and x.group(2) != "0":
        vInfo['par1'] = x.group(1) + ':' + x.group(2)
        vInfo['par2'] = float(x.group(1)) / float(x.group(2))
    else:
        vInfo['par1'], vInfo['par2'] = None, None

    # get dar.
    rezre = re.compile(r'.*Video: .+DAR ([0-9]+):([0-9]+).*')
    x = rezre.search(output)
    if x and x.group(1) != "0" and x.group(2) != "0":
        vInfo['dar1'] = x.group(1) + ':' + x.group(2)
    else:
        vInfo['dar1'] = None

    # get Audio Stream mapping.
    rezre = re.compile(r'([0-9]+[.:]+[0-9]+)(.*): Audio:(.*)')
    x = rezre.search(output)
    amap = []
    if x:
        for x in rezre.finditer(output):
            amap.append((x.group(1), x.group(2) + x.group(3)))
    else:
        amap.append(('', ''))
        debug('failed at mapAudio')
    vInfo['mapAudio'] = amap

    vInfo['par'] = None

    # get Metadata dump (newer ffmpeg).
    lines = output.split('\n')
    rawmeta = {}
    flag = False

    for line in lines:
        if line.startswith('  Metadata:'):
            flag = True
        else:
            if flag:
                if line.startswith('  Duration:'):
                    flag = False
                else:
                    try:
                        key, value = [x.strip() for x in line.split(':', 1)]
                        try:
                            value = value.decode('utf-8')
                        except:
                            if sys.platform == 'darwin':
                                value = value.decode('macroman')
                            else:
                                value = value.decode('cp1252')
                        rawmeta[key] = [value]
                    except:
                        pass

    vInfo['rawmeta'] = rawmeta

    data = metadata.from_text(inFile)
    for key in data:
        if key.startswith('Override_'):
            vInfo['Supported'] = True
            if key.startswith('Override_mapAudio'):
                audiomap = dict(vInfo['mapAudio'])
                newmap = shlex.split(data[key])
                audiomap.update(zip(newmap[::2], newmap[1::2]))
                vInfo['mapAudio'] = sorted(audiomap.items(),
                                           key=lambda (k, v): (k, v))
            elif key.startswith('Override_millisecs'):
                vInfo[key.replace('Override_', '')] = int(data[key])
            else:
                vInfo[key.replace('Override_', '')] = data[key]

    if cache:
        info_cache[inFile] = (mtime, vInfo)
    debug("; ".join(["%s=%s" % (k, v) for k, v in vInfo.items()]))
    return vInfo
Ejemplo n.º 9
0
    def get_image_ffmpeg(self, path, width, height, pshape, rot, attrs):
        ffmpeg_path = config.get_bin('ffmpeg')
        if not ffmpeg_path:
            return False, 'FFmpeg not found'

        fname = unicode(path, 'utf-8')
        if sys.platform == 'win32':
            fname = fname.encode('cp1252')

        if attrs and 'size' in attrs:
            result = attrs['size']
        else:
            status, result = self.get_size_ffmpeg(ffmpeg_path, fname)
            if not status:
                return False, result
            if attrs:
                attrs['size'] = result

        if rot in (90, 270):
            oldh, oldw = result
        else:
            oldw, oldh = result

        width, height = self.new_size(oldw, oldh, width, height, pshape)

        if rot == 270:
            filters = 'transpose=1,'
        elif rot == 180:
            filters = 'hflip,vflip,'
        elif rot == 90:
            filters = 'transpose=2,'
        else:
            filters = ''

        filters += 'format=yuvj420p,'

        neww, newh = oldw, oldh
        while (neww / width >= 50) or (newh / height >= 50):
            neww /= 2
            newh /= 2
            filters += 'scale=%d:%d,' % (neww, newh)

        filters += 'scale=%d:%d' % (width, height)

        cmd = [ffmpeg_path, '-i', fname, '-vf', filters, '-f', 'mjpeg', '-']
        jpeg_tmp = tempfile.TemporaryFile()
        ffmpeg = subprocess.Popen(cmd, stdout=jpeg_tmp,
                                  stdin=subprocess.PIPE)

        # wait configured # of seconds: if ffmpeg is not back give up
        limit = config.getFFmpegWait()
        if limit:
            for i in xrange(limit * 20):
                time.sleep(.05)
                if not ffmpeg.poll() == None:
                    break

            if ffmpeg.poll() == None:
                kill(ffmpeg)
                return False, 'FFmpeg timed out'
        else:
            ffmpeg.wait()

        jpeg_tmp.seek(0)
        output = jpeg_tmp.read()
        jpeg_tmp.close()

        if 'JFIF' not in output[:10]:
            output = output[:2] + JFIF_TAG + output[2:]

        return True, output