开发者

Using FFMPEG to reliably convert videos to mp4 for iphone/ipod and flash players

I need to convert videos for use in both a flash player and the iphone/ipod touch. I'm using the following batch script with ffmpeg:

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

This always outputs an mp4 file, and I can always play it on my PC. The videos also seem to play fine on my iphone 3GS. But with some input files it won't work for older iphone versions (3G and iPod touch).

Here's the ffmpeg output from one such file:

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 开发者_Python百科-> #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

My suspicion is that it has something to do with the audio encoding. If so, does anyone know how to force it to reencode the audio to the proper format?

Any other ideas?


WARNING: this answer is 10 years old and reported not to work anymore.


I think the issue is the H.264 level being level 4.2.

Some of the Apple devices only support up to 3.0.

Here's the FFMPEG settings I usually use:

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

You can adjust the rate, size and bitrate as needed. The important settings are in the baseline config param.


The ffmpeg wiki provides some useful up to date guidance on how to encode H.264 for particular devices. Here's an excerpt from Apple's docs with corresponding profiles:

iOS Compatability 
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


The listed ffmpeg settings didn't work for me (I don't seem to have the "baseline" preset listed), ffmpeg settings that don't reference baseline, I posted over here: iPhone "cannot play" .mp4 H.264 video file

Spoiler:

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

The official Apple reference on the subject: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html


Try this python script.

I wrote it for myself. Maybe you will find it useful too. It converts files to mp4.

Because of SO rules here the complete source code:

#!/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()


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

This disables some features but offers greater compatibility.

Also, here are some useful optional tags to add for working with the quality and file size:

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

-crf: 0-51

(preset modifies how long it takes to compress your video, with faster getting a bigger file size, and slower getting a smaller file size, whereas crf modifies the video quality, with higher quality having a bigger file size, and lower quality having a smaller file size.)


ffmpeg -i input.mov -c:v libx264 -pix_fmt yuv420p -profile:v main -crf 1 -preset medium -c:a aac -movflags +faststart output.mp4


Using FFMPEG to reliably convert videos to mp4 for iphone/ipod and flash players

ffmpeg.exe -i "Video.mp4" -vcodec libx264 -preset fast -profile:v baseline -lossless 1 -vf "scale=720:540,setsar=1,pad=720:540:0:0" -acodec aac -ac 2 -ar 22050 -ab 48k "Video (SD).mp4"


Got here because the simplest ffmpeg conversion approach was not producing an mp4 that would play on iOS for some reason.

Found settings that work for me in 2019 here:

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜