audioblacker发布:不加时间的过乐视不二压方法

庆祝期末考试结束的最好办法就是不歇气写10个小时的代码。
从pass的办法衍生出来,其实也不完全一样,只是思路有点像。
证明:

aaa:乐视云
aab:本地
都用ffmpeg直接解流不再封装,直接比对hash
# md5sum aaa.aac      aab.aac
ac9fb614c863349835f51d41b390d400  aaa.aac
ac9fb614c863349835f51d41b390d400  aab.aac
# md5sum aaa.h264     aab.h264
1670d9a42f32d8746994bd54129ba3d7  aaa.h264
1670d9a42f32d8746994bd54129ba3d7  aab.h264

代码:
github.com/cnbeining/audioblacker
再来一份扔下面。

#!/usr/bin/env python
#coding:utf-8
# Author:  Beining --<ACICFG>
# Contact: http://www.cnbeining.com/   |https://github.com/cnbeining/audioblacker
# Purpose: Patch video's audio part to bypass Letvcloud's transcode.
# Created: 08/21/2014
# LICENSE: GNU v2
import sys
import os
import os, sys, subprocess, shlex, re
from subprocess import call
import uuid
import math
import shutil
import getopt
#----------------------------------------------------------------------
def probe_file(filename):
    cmnd = ['ffprobe', '-show_format', '-pretty', '-loglevel', 'quiet', filename]
    p = subprocess.Popen(cmnd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    #print filename
    out, err =  p.communicate()
    #print out
    if err:
        print err
        return None
    return out
#----------------------------------------------------------------------
def time_to_sec(time_raw):
    """
    str->int
    ignore .*."""
    hr = int(time_raw.split(':')[0]) * 3600
    minute = int(time_raw.split(':')[1]) * 60
    sec = int(float(time_raw.split(':')[2]))
    return int(hr + minute + sec)
#----------------------------------------------------------------------
def get_abspath(filename):
    """"""
    return str(os.path.abspath(filename))
#----------------------------------------------------------------------
def process(filename, target_bitrate, audiobitrate, safe, outputfile):
    """str,int,int,str->?
    filename,outputfile comes with the path.
    safe=0: directly convert the file to m4a"""
    tmpdir = '/tmp/Audioblacker-' + str(uuid.uuid4())
    if not os.path.exists(tmpdir):
        os.makedirs(tmpdir)
    audio_format = ''
    audio_duration = ''
    audio_bitrate = ''
    video_format = ''
    video_duration = ''
    video_bitrate = ''
    #demux audio file
    # Add this to save time, for experienced users
    print('INFO: Checking audio and remux...')
    if safe == 0:
        #directly to m4a to save space
        os.system('ffmpeg -i \'' + filename + '\' -vn -c:a copy ' + tmpdir+'/audio.m4a' +'> /dev/null 2>&1')
        try:
            for line in probe_file(tmpdir+'/audio.m4a').split('\n'):
                #print(line)
                if 'duration' in line:
                    audio_duration = str(line.split('=')[1])
        except:
            print('ERROR: Cannot read audio file!')
            exit()
    else:
        os.system('ffmpeg -i \'' + filename + '\' -vn -c:a copy ' + tmpdir+'/audio.aac' +'> /dev/null 2>&1')
        try:
            for line in probe_file(tmpdir+'/audio.aac').split('\n'):
                    if 'format_name' in line:
                        audio_format = str(line.split('=')[1])
                    if 'duration' in line:
                        audio_duration = str(line.split('=')[1])
            print('INFO: Audio safe, converting to m4a and clean cache...')
            os.system('ffmpeg -i '+ tmpdir+'/audio.aac -vn -c:a copy ' + tmpdir+'/audio.m4a' +'> /dev/null 2>&1')
            try:
                os.remove(tmpdir+'/audio.aac')
            except:
                print('WARNING: Cannot remove the aac file now...')
        except:
            print('ERROR: Cannot read audio file!')
            exit()
        #In case someone screw the audio up
        if not 'aac' in audio_format:
            print('ERROR: You have to use AAC as audio format!')
            exit()
    #Check original file
    try:
        for line in probe_file(filename).split('\n'):
                if 'duration' in line:
                    video_duration = str(line.split('=')[1])
    except:
        print('ERROR: Cannot read video file!')
        exit()
    #Calc...
    #time_by_video
    print('INFO: Doing calculation...')
    video_duration_sec = time_to_sec(video_duration)
    video_size_byte = int(os.path.getsize(filename))
    audio_duration_sec = time_to_sec(audio_duration)
    audio_size_byte = int(os.path.getsize(tmpdir+'/audio.m4a'))
    #!!!!!FLOAT DUE TO ACCURACY!!!!!
    target_byterate = target_bitrate / 8
    target_audiobyterate = audiobitrate / 8
    time_video = int(math.ceil((video_size_byte - target_byterate * video_duration_sec) / (target_byterate - 300)))
    time_audio = int(math.ceil((audio_size_byte - target_audiobyterate * audio_duration_sec) / (target_audiobyterate - 300)))
    if time_audio < 0 and time_video < 0:
        print('ERROR: Cannot calculate target, your target bitrate is higher than the original file!')
        shutil.rmtree(tmpdir)
        exit()
    if time_audio == 0 and time_video == 0:
        time_patch = 60
    elif time_audio > time_video:
        time_patch = time_audio + 10
    else:
        time_patch = time_video + 10
    #Make audio patch
    f = open(tmpdir + '/audiocode.txt', 'w')
    ff = 'file \'' + tmpdir + '/audio.m4a\'' + '\n'
    os.getcwd()
    py_path = sys.path[0]
    os.chdir(py_path)
    for i in range(time_patch):
        ff = ff + 'file \'' + str(os.getcwd()) + '/'+ '1sblack.m4a\'' + '\n'
    ff = ff.encode("utf8")
    f.write(ff)
    f.close()
    print('INFO: Concating audios...')
    os.system('ffmpeg -f concat -i '+ tmpdir + '/audiocode.txt -c copy '+ tmpdir +'/audiopatched.m4a'+'> /dev/null 2>&1')
    print('INFO: Making final output file...')
    os.system('ffmpeg -i ' + filename + ' -i ' + tmpdir +'/audiopatched.m4a -c copy  -map 0:0 -map 1:0 '+outputfile +'> /dev/null 2>&1')
    print('Done!')
    #clean up
    try:
        shutil.rmtree(tmpdir)
    except:
        print('ERROR: Cannot remove temp dir, do it by yourself!')
#----------------------------------------------------------------------
def usage():
    """"""
    print('''Usage:
    python audioblacker.py (-h) (-i input.mp4) (-o output.mp4) (-b 1990000) (-a 120000) (-s 1)
    -h: Default: None
        Help.
    -i: Default: Blank
        Input file.
        If the file and audioblacker are not under the same path,
        it is suggested to use absolute path to avoid possible failure.
    -o Default: input-filename.black.mp4
       Output file.
       Would be in the same folder with the original file if not specified.
    -b: Default: 1990000
        Target bitrate.
    -a: Default: 110000
        Target audio bitrate.
    audioblacker would calculate both of the required black time,
    and choose the larger one to make sure your convert is successful.
    -s: Default: 1
        Use safe mode.
        audioblacker would check whether the file's audio is AAC.
        Disabling would save you some space and time,
        if you know what you are doing.
        ''')
#----------------------------------------------------------------------
if __name__=='__main__':
    argv_list = []
    argv_list = sys.argv[1:]
    filename = ''
    target_bitrate = 1990000
    outputfile = ''
    safe = 1
    audiobitrate = 110000
    try:
        opts, args = getopt.getopt(argv_list, "hi:b:a:o:s:", ['help', 'input','bitrate', 'audiobitrate'
                                                           'outputfile', 'safe'])
    except getopt.GetoptError:
        usage()
        exit()
    for o, a in opts:
        if o in ('-h', '--help'):
            usage()
            exit()
        elif o in ('-i', '--input'):
            filename = a
            try:
                argv_list.remove('-i')
            except:
                break
        elif o in ('-b', '--bitrate'):
            target_bitrate = int(a)
            try:
                argv_list.remove('-b')
            except:
                break
        elif o in ('-a', '--audiobitrate'):
            audiobitrate = int(a)
            try:
                argv_list.remove('-a')
            except:
                break
        elif o in ('-o', '--outputfile'):
            outputfile = a
            try:
                argv_list.remove('-o')
            except:
                break
        elif o in ('-s', '--safe'):
            safe = int(a)
            try:
                argv_list.remove('-s')
            except:
                break
    if filename == '':
        print('ERROR: No input file!')
        exit()
    if outputfile == '':
        outputfile = filename.split('.')[0] + '.black.mp4'
    process(filename, target_bitrate, audiobitrate, safe, outputfile)

 

48 thoughts on “audioblacker发布:不加时间的过乐视不二压方法

    1. Beining Post author

      本身就已经不精确了。
      因为
      1)音频是拿Audition弄的,但是其实是1s69
      2)音频不是300字节,封装后变成1.02k
      所以肯定会有差别。
      但是不碍事。

      Reply
    2. Beining Post author

      当时想用一个FLV解析的包,但是发现不行,还不如ffprobe自己算。
      这个包是2.7的,就这样了。
      要是乐意我也可以改改变成3.3,你看里面语法基本都是3.3的。。。

      Reply
    1. dant

      明白了,
      你接上去那段是无效流,拖时间拖码率但是不能解码
      我接上去那段是有效流,拖时间不拖码率能解码
      话说可以现场搞个空音频啊,用lavfi和ffmpeg的原装AAC编码器

      Reply
      1. Beining Post author

        我那段是有效流啊。。。
        AU录制的,不是无效的。
        我认为吧,真的不能指望大家电脑上有lavfi。很多人ffmpeg都编译不明白。。。。。。

        Reply
        1. dant

          那真是被玩坏了。。
          其实直接用原来的音频流就行了。。
          接AAC跟接H264差不多,要相同采样率、声道数和Profile才能解码

          Reply
          1. Beining Post author

            那我们起码还有VFR可以用。
            如果连VFR都不能用了就麻烦一些了,Linux弄AVS费劲。
            可能弄一堆1s的各种分辨率文件往上安?或者另想办法?
            但是乐视正常传,码率也可以达到6M。其实大点没有问题,只要不是10M+的无脑后黑就行,但是1080P弄10+M也不大容易。。。

          2. Beining Post author

            ffmpeg -filter_complex 'color=black:s=640×480:d=1' -c:v libx264 black480p.mp4
            ffmpeg version 2.3.git Copyright (c) 2000-2014 the FFmpeg developers
            built on Aug 3 2014 13:42:36 with gcc 4.6 (Ubuntu/Linaro 4.6.3-1ubuntu5)
            configuration: --prefix=/root/ffmpeg_build --extra-cflags=-I/root/ffmpeg_build/include --extra-ldflags=-L/root/ffmpeg_build/lib --bindir=/root/bin --enable-gpl --enable-libass --enable-libfdk-aac --enable-libfreetype --enable-libmp3lame --enable-libx264 --enable-nonfree
            libavutil 52. 94.100 / 52. 94.100
            libavcodec 55. 71.100 / 55. 71.100
            libavformat 55. 50.100 / 55. 50.100
            libavdevice 55. 13.102 / 55. 13.102
            libavfilter 4. 11.102 / 4. 11.102
            libswscale 2. 6.100 / 2. 6.100
            libswresample 0. 19.100 / 0. 19.100
            libpostproc 52. 3.100 / 52. 3.100
            [color @ 0x24a9380] Unable to parse option value "640×480" as image size
            Last message repeated 1 times
            [color @ 0x24a9380] Error setting option s to value 640×480.
            [Parsed_color_0 @ 0x24a8e40] Error applying options to the filter.
            [AVFilterGraph @ 0x24a9b80] Error initializing filter 'color' with args 'black:s=640×480:d=1'
            Error configuring filters.

            啊拉。。。。。。

      1. dant

        直接调用了FFmpeg的API,好像libav*系列还没有Python binding(不过可以看一下LLVM的黑科技,把C编译成Python什么的。。)
        话说你这站不用梯子又上不了了。。

        Reply
          1. Beining Post author

            Openshift是AWS的IP,一般来说投鼠忌器。。。
            那我该说啥还说啥。GFW认证也不是什么坏事。
            被GFW认证代表我已经让人害怕了 那也不错啊。

    1. Beining Post author

      找了一圈没看见python有啥好的处理flv的库。。。这就麻烦了。
      我又是语言苦手,C什么的只能靠猜,一个字写不出,读懂都费劲。。。
      ffmpeg与python好久之前有个封装,估计早不能用了。
      只能继续碰碰运气了。。。
      现在的这个libavbuggyduration(为什么我们都喜欢把程序的名字起得那么长呢)的3倍速原理是和之前的FlvPatcher的原理是一样的么?传乐视正常么?
      经过测试,乐视的处理顺序应该是:
      1.Mediainfo???看信息,决定是不是二压原画
      2.demux,压,remux
      3.YAMDI(雅蠛蝶)写进去metadata。
      FYI。

      Reply
      1. dant

        这个倍速才是正确的实现,乐视正常,下载(手机)党骂娘。FlvPatcher那个不能用的。
        而且说不定哪天FFmpeg一更新好心不让这样玩了就杯具了,尽快编译个静态版吧。。
        这个东西目前只在我的Arch Linux上编译成功过,VPS上的Debian不行(LibAV的libavutil太新,没错,就是太!新!),交叉编译的话MinGW-w64链接失败,Windows上编译速度太慢configure ffmpeg的时候就掐掉了。。
        其实你也可以像FlvBugger一样自己处理封装,只不过Python的效率肯定比不上.NET。
        话说渣浪和乐视怎么都在用YAMDI。。

        Reply
        1. Beining Post author

          找了几圈死活看不见有什么好的python的对得起人的flv库。
          FlvPatcher的实现我又读了一下,和libavbuggyduration到底差在哪里呢?原谅我C实在是不会。。。
          应该就是所有的timecode都乘上一个数,最后一帧保留?还有别的我没看见的坑么?
          (我也不指望能make出来了。。。看样坑很大)

          Reply
          1. dant

            倍速需要处理音频流,把所有帧的时间码都乘上一个数,然后Flash还是能按原来的速度播放(这跟VLC不跳过后黑但是Flash跳过是一个道理)。音频也是一帧一帧地放着,所以就能动手脚了。
            FlvPatcher只能处理视频流,当时从blacker.sh改了几行就变成3xspeed.sh了,然后传上渣浪发现不行。。
            还有FFmpeg API的一些坑,导致只能处理相同封装格式(原型是FFmpeg的范例程序),还有av_copy_packet和av_copy_packet_side_data什么的。。坑了我几小时,就差重构了。。。(不过跟用户也没什么关系)
            构建的话,我到时写个脚本,先拖个FFmpeg的git版下来编译一下。。

          2. Beining Post author

            ffmpeg有Linux binary:
            ffmpeg.gusari.org/static/

            # ./ffmpeg
            ffmpeg version N-60696-g38a08e0 Copyright (c) 2000-2014 the FFmpeg developers
            built on Feb 17 2014 15:07:16 with gcc 4.6 (Debian 4.6.3-1)
            configuration: --prefix=/root/ffmpeg-static/64bit --extra-cflags='-I/root/ffmpeg-static/64bit/include -static'
            --extra-ldflags='-L/root/ffmpeg-static/64bit/lib -static' --extra-libs='-lxml2 -lexpat -lfreetype'
            --enable-static --disable-shared --disable-ffserver --disable-doc --enable-bzlib --enable-zlib
            --enable-postproc --enable-runtime-cpudetect --enable-libx264 --enable-gpl --enable-libtheora
            --enable-libvorbis --enable-libmp3lame --enable-gray --enable-libass --enable-libfreetype --enable-libopenjpeg
            --enable-libspeex --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-version3 --enable-libvpx
            libavutil 52. 64.100 / 52. 64.100
            libavcodec 55. 52.102 / 55. 52.102
            libavformat 55. 33.100 / 55. 33.100
            libavdevice 55. 10.100 / 55. 10.100
            libavfilter 4. 1.102 / 4. 1.102
            libswscale 2. 5.101 / 2. 5.101
            libswresample 0. 17.104 / 0. 17.104
            libpostproc 52. 3.100 / 52. 3.100
            Hyper fast Audio and Video encoder
            usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...
            Use -h to get full help or, even better, run 'man ffmpeg'

            那么理论上,如果用ffmpeg导出所有帧的timecode,然后统一扩大,那么不就可以了么?blacker是只管视频,那么修改应该也不大。。。

          3. dant

            如果能导出音频时间码还能封进去就行。。之前Google翻了十几页也找不到。。。
            或者试试音频偏移?

          4. dant

            不对。。你要的是倍速啊。。
            那几个倍速都是直接搞FLV的(FLV格式还算是比较简单的,Google一下就能找到Adobe的文档)

          5. Beining Post author

            MP4不也弄了么。。。
            但是真就找不到合适的工具了。
            我现在也在考虑怎么弄音频timecode,但是不大成功。。。。

          6. dant

            BPS上搞了一下,大概是这样?//cirno.xyz/videos/test3.mp4 和 //cirno.xyz/videos/test3.reencode.mp4
            确实是处理了两个时间码,不过三压版的音频是连续的(二压版手机播放不能)

          7. Beining Post author

            恩。。。
            从videospeeder(是的,我重新写了一个倍速)来看,貌似因为时间码,怎么着二压后都会不正常。
            那么 再加一次remux会有变化么?

          8. Beining Post author

            要是时间码不对了,那么怎么二压肯定都不行了。
            所以这个就当立此存照吧。
            然后我把小丸的办法搬到了Linux:
            github.com/cnbeining/videoblacker
            但是我音频视频都处理,小丸只处理视频(之前没拆代码还真不知道这回事。。。。。)
            不信邪了,看他怎么封杀。。。上人工审核不算本事。

          9. Beining Post author

            恩。。。因为音频可以用ffmpeg检测是不是后面有后黑,视频也可以检测是不是是黑的,所以这个办法有可能失效。
            但是无妨,我们还可以给他接一段正弦波或者白噪音/粉红噪音上去。。。或者加上一张漏尿图或者小圆本子什么的。
            骗机器比骗人容易的多啊。。。

Leave a Reply

Your email address will not be published. Required fields are marked *