使用FFMPEG将视频可靠地转换为mp4,用于iphone / ipod和flash播放器

问题描述 投票:17回答:6

我需要转换视频以用于flash播放器和iphone / ipod touch。我在ffmpeg中使用以下批处理脚本:

@echo off
ffmpeg.exe -i %1 -s qvga -acodec libfaac -ar 22050 -ab 128k -vcodec libx264 -threads 0 -f ipod %2

这总是输出一个mp4文件,我总是可以在我的电脑上播放它。我的iphone 3GS上的视频似乎也很好用。但是对于一些输入文件,它不适用于较旧的iPhone版本(3G和iPod touch)。

这是来自一个这样的文件的ffmpeg输出:

D:\ffmpeg>encode.bat d:\temp\recording.flv d:\temp\out.m4v
FFmpeg version SVN-r18709, Copyright (c) 2000-2009 Fabrice Bellard, et al.
  configuration: --enable-memalign-hack --prefix=/mingw --cross-prefix=i686-ming
w32- --cc=ccache-i686-mingw32-gcc --target-os=mingw32 --arch=i686 --cpu=i686 --e
nable-avisynth --enable-gpl --enable-zlib --enable-bzlib --enable-libgsm --enabl
e-libfaac --enable-libfaad --enable-pthreads --enable-libvorbis --enable-libtheo
ra --enable-libspeex --enable-libmp3lame --enable-libopenjpeg --enable-libxvid -
-enable-libschroedinger --enable-libx264
  libavutil     50. 3. 0 / 50. 3. 0
  libavcodec    52.27. 0 / 52.27. 0
  libavformat   52.32. 0 / 52.32. 0
  libavdevice   52. 2. 0 / 52. 2. 0
  libswscale     0. 7. 1 /  0. 7. 1
  built on Apr 28 2009 04:04:42, gcc: 4.2.4
[flv @ 0x187d650]skipping flv packet: type 18, size 164, flags 0
Input #0, flv, from 'd:\temp\recording.flv':
  Duration: 00:00:07.17, start: 0.001000, bitrate: N/A
    Stream #0.0: Video: flv, yuv420p, 320x240, 1k tbr, 1k tbn, 1k tbc
    Stream #0.1: Audio: nellymoser, 44100 Hz, mono, s16
[libx264 @ 0x13518b0]using cpu capabilities: MMX2 SSE2Fast SSSE3 FastShuffle SSE
4.2
[libx264 @ 0x13518b0]profile Baseline, level 4.2
Output #0, ipod, to 'd:\temp\out.m4v':
    Stream #0.0: Video: libx264, yuv420p, 320x240, q=2-31, 200 kb/s, 1k tbn, 1k
tbc
    Stream #0.1: Audio: libfaac, 22050 Hz, mono, s16, 128 kb/s
Stream mapping:
  Stream #0.0 -> #0.0
  Stream #0.1 -> #0.1
Press [q] to stop encoding
frame=   90 fps=  0 q=-1.0 Lsize=     128kB time=6.87 bitrate= 152.4kbits/s
video:92kB audio:32kB global headers:1kB muxing overhead 2.620892%
[libx264 @ 0x13518b0]slice I:8     Avg QP:29.62  size:  7047
[libx264 @ 0x13518b0]slice P:82    Avg QP:30.83  size:   467
[libx264 @ 0x13518b0]mb I  I16..4: 17.9%  0.0% 82.1%
[libx264 @ 0x13518b0]mb P  I16..4:  0.6%  0.0%  0.0%  P16..4: 23.1%  0.0%  0.0%
 0.0%  0.0%    skip:76.3%
[libx264 @ 0x13518b0]final ratefactor: 57.50
[libx264 @ 0x13518b0]SSIM Mean Y:0.9544735
[libx264 @ 0x13518b0]kb/s:8412.6

我怀疑它与音频编码有关。如果是这样,有没有人知道如何强制它将音频重新编码为正确的格式?

还有其他想法吗?

iphone ffmpeg video-encoding
6个回答
4
投票

我认为问题是H.264级别是4.2级。

有些Apple设备最多只支持3.0。

这是我经常使用的FFMPEG设置:

ffmpeg -i YOUR-INPUT.wmv -s qvga -b 384k -vcodec libx264 -r 23.976 -acodec libfaac -ac 2 -ar 44100 -ab 64k -vpre baseline -crf 22 -deinterlace -o YOUR-OUTPUT.MP4

您可以根据需要调整速率,大小和比特率。重要设置位于基线配置参数中。


2
投票

列出的ffmpeg设置对我不起作用(我似乎没有列出“基线”预设),ffmpeg设置不参考基线,我在这里发布:iPhone "cannot play" .mp4 H.264 video file

扰流板:

ffmpeg -i INPUT -s 320x240 -r 30000/1001 -b 200k -bt 240k -vcodec libx264 -coder 0 -bf 0 -refs 1 -flags2 -wpred-dct8x8 -level 30 -maxrate 10M -bufsize 10M -acodec libfaac -ac 2 -ar 48000 -ab 192k OUTPUT.mp4

关于这个主题的Apple官方参考:http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html


2
投票

试试this python script

我是为自己写的。也许你会发现它也很有用。它将文件转换为mp4。

由于SO规则,这里有完整的源代码:

#!/usr/bin/python

# Copyright (C) 2007-2010 CDuke
# This program is free software. You may distribute it under the terms of
# the GNU General Public License as published by the Free Software
# Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but

# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# This program converts video files to mp4, suitable to be played on an iPod
# or an iPhone. It is careful about maintaining the proper aspect ratio.

from __future__ import division
from datetime import datetime
import sys
import argparse
import os
import re
import shlex
import time
from subprocess import Popen, PIPE

DEFAULT_ARGS = '-f mp4 -y -vcodec libxvid -maxrate 1000k -mbd 2 -qmin 3 -qmax 5 -g 300 -bf 0 -acodec libfaac -ac 2 -flags +mv4 -trellis 2 -cmp 2 -subcmp 2'
#DEFAULT_ARGS = '-f mp4 -y -vcodec mpeg4 -vtag xvid -maxrate 1000k -mbd 2 -qmin 3 -qmax 5 -g 300 -bf 0 -acodec libfaac -ac 2 -r 30000/1001 -flags +mv4 -trellis 2 -cmp 2 -subcmp 2'
#DEFAULT_ARGS = '-y -f mp4 -vcodec libxvid -acodec libfaac'
DEFAULT_BUFSIZE = '4096k'
DEFAULT_AUDIO_BITRATE = '128k'
DEFAULT_VIDEO_BITRATE = '400k'
FFMPEG = '/usr/bin/ffmpeg'

class device:
    '''Describe properties of device'''
    def __init__(self, name, width, height):
        self.name = name
        self.width = width
        self.height = height

class videoFileInfo:
    def __init__(self, width, height, duration):
        self.width = width
        self.height = height
        self.duration = duration

devices = [device('ipod', 320, 240), device('iphone', 480, 320),
device('desire', 800, 480)]

def getOutputFileName(inputFileName, outDir):
    if outDir == None:
        outFileName = os.path.splitext(inputFileName)[0] + '.mp4'
    else:
        outFileName = os.path.join(outDir, os.path.basename(inputFileName))
    return outFileName

def getVideoFileInfo(fileName):
    p = Popen([FFMPEG, '-i', fileName], stdout = PIPE, stderr = PIPE)
    fileInfo = p.communicate()[1]
    videoRes = re.search(b'Video:.+ (\d+)x(\d+)', fileInfo)
    w = float(videoRes.group(1))
    h = float(videoRes.group(2))
    duratMatch = re.search(b'Duration:\s+(\d+):(\d+):(\d+)\.(\d+)', fileInfo)
    duration = float(duratMatch.group(1)) * 3600
    duration += float(duratMatch.group(2)) * 60
    duration += float(duratMatch.group(3))
    duration += float(duratMatch.group(4)) / 10
    fileInfo = videoFileInfo(w, h, duration)
    return fileInfo

def getArguments(width, height, aspect):
    args = {}
    w = width
    h = w // aspect
    h -= (h % 2)
    if h <= height:
        pad = (height - h) // 2
        pad -= (pad % 2)
        pady = pad
        padx = 0
    else:
        # recalculate using the height as the baseline rather than the width
        h = height
        w = int(h * aspect)
        width -= (width % 2)
        pad = (width - w) // 2
        pad -= (pad % 2)
        padx = pad
        pady = 0

    args['width'] = w
    args['height'] = h
    args['padx'] = padx
    args['pady'] = pady
    return args

def getProgressBar(perc):
    convInfo = 'Converted: [{}] {:.2%} \r'
    num_hashes = round(perc * 100 // 2)
    bar = '=' * num_hashes + ' ' * (50 - num_hashes)
    return convInfo.format(bar, perc)

def convert(inputFileName, outputFileName, args, audioBitrate, videoBitrate, devWidth, devHeight, aspect, duration):
    cmd = '{ffmpeg} -i {inFile} {defaultArgs} -bufsize {bufsize} -s {width}x{height} -vf "pad={devWidth}:{devHeight}:{padx}:{pady},aspect={aspect}" -ab {audioBitrate} -b {videoBitrate} {outFile}'.format(ffmpeg=FFMPEG, inFile=inputFileName, defaultArgs=DEFAULT_ARGS, bufsize=DEFAULT_BUFSIZE, devWidth=devWidth, devHeight=devHeight, padx=args['padx'], pady=args['pady'], width=args['width'], height=args['height'], aspect=aspect, audioBitrate=audioBitrate, videoBitrate=videoBitrate, outFile=outputFileName)
#    cmd = '{ffmpeg} -i {inFile} {defaultArgs} -bufsize {bufsize} -s {width}x{height} -ab {audioBitrate} -b {videoBitrate} {outFile}'.format(ffmpeg=FFMPEG, inFile=inputFileName, defaultArgs=DEFAULT_ARGS, bufsize=DEFAULT_BUFSIZE, width=args['width'], height=args['height'], audioBitrate=audioBitrate, videoBitrate=videoBitrate, outFile=outputFileName)
    print(cmd)
    print()
    start = datetime.today()
    print('Converting started at ' + str(start))
    conv = Popen(shlex.split(cmd), shell=False, stdout=PIPE, stderr=PIPE)
    while conv.poll() is None:
       out = os.read(conv.stderr.fileno(), 2048)
       last = out.splitlines()[-1]
       timeMatch = re.search(b'time=([^\s]+)', last)
       if timeMatch:
           timeDone = float(timeMatch.group(1))
           perc = timeDone / duration
           if sys.version_info > (3, 0):
               exec("print(getProgressBar(perc), end='')")
           else:
               exec("print getProgressBar(perc),")
           sys.stdout.flush()
#       else:
#           print(out)
       time.sleep(0.5)
    print(getProgressBar(1))
    end = datetime.today()
    print('Converting ended at ' + str(end))
    print('Spended time: ' + str(end - start))

class mp4Converter(argparse.Action):
    def __call__(self, parser, namespace, values, option_string = None):
        outdir = namespace.outdir
        for f in values:
            outFileName = getOutputFileName(f.name, outdir)
            fileInfo = getVideoFileInfo(f.name)
            aspect = fileInfo.width / fileInfo.height
            dev = next(d for d in devices if d.name == namespace.device)
            args = getArguments(dev.width, dev.height, aspect)
            convert(f.name, outFileName, args, namespace.AUDIO_BITRATE, namespace.VIDEO_BITRATE, dev.width, dev.height, aspect, fileInfo.duration)
            print('file "{0}" converted successful'.format(f.name))

opts = argparse.ArgumentParser(
    description = 'Converter to MP4',
    epilog = 'made by CDuke 2010')
opts.add_argument('-V','--version',
                  action = 'version',
                  version = '0.0.1')
opts.add_argument('-v', '--verbose',
                  action = 'store_true',
                  default = False,
                  help = 'verbose')
opts.add_argument('-a', '--audio',
                  dest = 'AUDIO_BITRATE',
                  default = DEFAULT_AUDIO_BITRATE,
                  help = 'override default audio bitrate {0}'.format(DEFAULT_AUDIO_BITRATE))
opts.add_argument('-b', '--video',
                  dest = 'VIDEO_BITRATE',
                  default = DEFAULT_VIDEO_BITRATE,
                  help = 'override default video bitrate {0}'.format(DEFAULT_VIDEO_BITRATE))
opts.add_argument('-d', '--device',
                  choices = [d.name for d in devices],
                  default = 'ipod',
                  help = 'device that will play video')
opts.add_argument('-o', '--outdir',
                  help = 'write files to given directory')
opts.add_argument('file',
                  nargs = '+',
                  type = argparse.FileType('r'),
                  action = mp4Converter,
                  help = 'file that will be converted')

opts.parse_args()

1
投票

ffmpeg wiki提供了一些有关如何为特定设备编码H.264的最新指南。这是一段摘录:

iOS Compatability ([​source][2])
Profile  Level Devices                                                     Options
Baseline 3.0  All devices                                                  -profile:v baseline -level 3.0
Baseline 3.1  iPhone 3G and later, iPod touch 2nd generation and later     -profile:v baseline -level 3.1
Main     3.1  iPad (all vers), Apple TV 2 and later, iPhone 4 and later    -profile:v main -level 3.1
Main     4.0  Apple TV 3 and later, iPad 2 and later, iPhone 4s and later  -profile:v main -level 4.0
High     4.0  Apple TV 3 and later, iPad 2 and later, iPhone 4s and later  -profile:v high -level 4.0
High     4.1  iPad 2 and later, iPhone 4s and later, iPhone 5c and later   -profile:v high -level 4.1
High     4.2  iPad Air and later, iPhone 5s and later                      -profile:v high -level 4.2

1
投票
ffmpeg -i test.mov -profile:v baseline -level 3.0  test.mp4

这会禁用某些功能,但可提供更高的兼容性。

此外,以下是一些有用的可选标记,用于处理质量和文件大小:

-preset: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo

-crf: 0-51

(预设修改了压缩视频所需的时间,更快地获得更大的文件大小,更慢地获得更小的文件大小,而crf修改视频质量,更高的质量具有更大的文件大小,更低的质量具有更小的文件文件大小。)


-1
投票

到这里是因为最简单的ffmpeg转换方法并没有产生一个可以在iOS上播放的mp4。

在2019年找到适合我的设置:

https://gist.github.com/jaydenseric/220c785d6289bcfd7366

ffmpeg -i input.mov -c:v libx264 -pix_fmt yuv420p -profile:v baseline -level 3.0 -crf 22 -preset veryslow -vf scale=1280:-2 -c:a aac -strict experimental -movflags +faststart -threads 0 output.mp4
© www.soinside.com 2019 - 2024. All rights reserved.