iOS--FFmpeg篇(一)

   最近公司需要做一個flv播放器,而且只要播放聲音即可。剛開始找到了Vitamio,發現用它來播放flv確實不怎麼樣,而且沒有iOS的源碼,感覺很不讓人放心。於是開始了編譯FFmpeg的歷程。話說Github上有一鍵編譯的各種腳本,但是難點不是編譯,而是如何設定參數來滿足公司的需求。要知道FFmpeg支持的編碼非常之多,而我僅僅關心flv。

發現 一個FFmpeg大神,地址:http://blog.csdn.net/leixiaohua1020


下面我來一一介紹幾種一鍵編譯方案,希望大家喜歡。


(一)Vitamio維護的開源項目->傳送門

       這個項目,好就好在它同時具有iOS和android的一鍵編譯腳本。

       首先我們拿到項目之後,打開build_ios.sh文件。我們會看到第一句話就是去尋找build_ios_local.sh文件,好吧,這個文件需要我們手動複製到$HOME目錄下,至於裏面的內容,也很簡單。配置一下路徑和SDK版本,例如下面這樣:

# Please make a copy for this file named 'build_ios_local.sh', and copy to $HOME
# directory, then fix out bellow configure lines to fit yourself.

## DEVELOPER & SDKVERSION setting sample,
export DEVELOPER="/Users/nuoerlz/Applications/Xcode.app/Contents/Developer"
export SDKVERSION="6.1"

記得將註釋#解開。

然後,我們來繼續看build_ios.sh文件,在20幾行,我們發現它還引入了SSL的支持,如果我們不想支持SSL,我們可以去掉。當然,記得刪除所有SSL的代碼,要不然後邊編譯會非常麻煩。再接下來我們會看到兩個比較大的函數:doConfigureOnline() 和 doConfigureAll()這兩個函數,是用來配置ffmpeg 編譯選項的,當然,我們可以只編譯其中一個版本,至於選項的問題,請查看這篇文章->傳送門  。在第205行,我們看到build_archs="arm64 x86_64 armv7 armv7s i386",它支持的版本還是比較全的。起碼arm64是我們目前想要的。

       另外,這個腳本還需要一個命令的支持gas-preprocessor.pl  ,這個命令至關重要,而且它已經經過修改,和後面我們要用到的同名文件有一定的區別。詳情請看https://raw.github.com/libav/gas-preprocessor/master/gas-preprocessor.pl   你需要將這個文件放到/usr/bin/目錄下。

      如果不出意外,腳本就可以順利的運行結束了,我們會在日期文件夾的univslib目錄裏邊發現合併在一起的FFmpeg.a文件。


 (二)使用一鍵編譯項目FFmpeg-iOS-build-script ->傳送門

     這同樣是一個sh文件,項目使用起來也很方便。通過代碼我們可以看到,它會自動安裝yasm 、gas-preprocessor.pl甚至會自己下載FFmpeg源代碼,我們只需要等着就行了。不過它內部設定的參數有點少,如果想我一樣需求明確的話,最好還是修改一二。這樣編譯出來的包會小很多。

使用方法:

    編譯所有版本:
    ./build-ffmpeg.sh

    只編譯arm64版本:
    ./build-ffmpeg.sh arm64

    編譯 armv7 and x86_64 (64-bit simulator)版本:
    ./build-ffmpeg.sh armv7 x86_64

    合併包:
    ./build-ffmpeg.sh lipo

   (三)使用開源的 kxmovie -> 傳送門

     這是一個開源的ios播放器程序,支持基本上所有類型的音頻和視頻,你需要做的就是把github上的代碼clone下來,運行Rakefile文件,它會自動下載FFmpeg的相關文件,接下來你只需要坐等編譯成功即可。

$ rake

     編譯完成之後,你就可以運行kxmovie.xcworkspace來看看成果了~~

編譯好的ffmpeg默認只包含:armv7、armv7s、arm64、i386,如果你想繼續集成 x86_64(加上它之後會非常大!) ,可以參考下面的rakefile:


require "pathname"
require "fileutils"

def system_or_exit(cmd, stdout = nil)
  puts "Executing #{cmd}"
  cmd += " >#{stdout}" if stdout
  system(cmd) or raise "******** Build failed ********"
end

## build ffmpeg

SDK_VERSION='8.2'
MIN_IOS_VERSION='7.0'

XCODE_PATH='/Applications/Xcode.app/Contents/Developer/Platforms'
GCC_PATH='/Applications/XCode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang'
LIB_PATH='/usr/lib/system'
GASPREP_DEST_PATH='/usr/local/bin'
PLATOFRM_PATH_SIM ='/iPhoneSimulator.platform'
PLATOFRM_PATH_IOS ='/iPhoneOS.platform'
SDK_PATH_SIM="/Developer/SDKs/iPhoneSimulator#{SDK_VERSION}.sdk"
SDK_PATH_IOS="/Developer/SDKs/iPhoneOS#{SDK_VERSION}.sdk"

MY_SELF_PLATFORM_ARGS = [
'--disable-shared',
'--disable-small',
'--disable-runtime-cpudetect',
'--disable-programs',
'--enable-network',
'--disable-muxers',
'--disable-filters',
'--enable-parsers',
'--disable-parser=hevc',
'--enable-protocol=file',
'--enable-protocol=http',
'--enable-demuxer=flv',
'--enable-demuxer=mp3',
'--enable-decoder=flv',
'--enable-decoder=mp3',
'--enable-cross-compile',
]

FFMPEG_BUILD_ARGS_SIM = [
'--disable-everything',
'--assert-level=2',
'--disable-mmx',
'--arch=i386',
'--cpu=i386',
"--extra-ldflags='-arch i386 -miphoneos-version-min=#{MIN_IOS_VERSION}'",
"--extra-cflags='-arch i386 -miphoneos-version-min=#{MIN_IOS_VERSION}'",
'--disable-asm',
]

FFMPEG_BUILD_ARGS_SIM_64 = [
'--assert-level=2',
'--disable-mmx',
'--arch=x86_64',
'--cpu=x86_64',
"--extra-ldflags='-arch x86_64 -miphoneos-version-min=7.0'",
"--extra-cflags='-arch x86_64 -miphoneos-version-min=7.0'",
'--disable-asm',
]

FFMPEG_BUILD_ARGS_ARMV7 = [
'--disable-everything',
'--arch=arm',
'--cpu=cortex-a8',
'--enable-pic',
"--extra-cflags='-arch armv7 -miphoneos-version-min=#{MIN_IOS_VERSION}'",
"--extra-ldflags='-arch armv7 -miphoneos-version-min=#{MIN_IOS_VERSION}'",
"--extra-cflags='-mfpu=neon -mfloat-abi=softfp'",
'--enable-neon',
# '--disable-neon',
'--enable-optimizations',
'--disable-debug',
'--disable-armv5te',
'--disable-armv6',
'--disable-armv6t2',
'--enable-small',
]

FFMPEG_BUILD_ARGS_ARMV7S = [
'--disable-everything',
'--arch=arm',
'--cpu=cortex-a9',
'--enable-pic',
"--extra-cflags='-arch armv7s -miphoneos-version-min=#{MIN_IOS_VERSION}'",
"--extra-ldflags='-arch armv7s -miphoneos-version-min=#{MIN_IOS_VERSION}'",
"--extra-cflags='-mfpu=neon -mfloat-abi=softfp'",
'--enable-neon',
# '--disable-neon',
'--enable-optimizations',
'--disable-debug',
'--disable-armv5te',
'--disable-armv6',
'--disable-armv6t2',
'--enable-small',
]

FFMPEG_BUILD_ARGS_ARM64 = [
'--disable-everything',
'--arch=arm64',
# '--cpu=cortex-a9',
'--enable-pic',
"--extra-cflags='-arch arm64 -miphoneos-version-min=#{MIN_IOS_VERSION}'",
"--extra-ldflags='-arch arm64 -miphoneos-version-min=#{MIN_IOS_VERSION}'",
"--extra-cflags='-mfpu=neon -mfloat-abi=softfp'",
'--enable-neon',
# '--disable-neon',
'--enable-optimizations',
'--disable-debug',
'--disable-armv5te',
'--disable-armv6',
'--disable-armv6t2',
'--enable-small',
]

FFMPEG_BUILD_ARGS = [
'--disable-ffmpeg',
'--disable-ffplay',
'--disable-ffserver',
'--disable-ffprobe',
'--disable-doc',
'--disable-bzlib',
'--target-os=darwin',
'--enable-cross-compile',
#'--enable-nonfree',
# '--enable-gpl',
'--enable-version3',
]

FFMPEG_LIBS = [
'libavcodec',
'libavformat',
'libavutil',
'libswscale',
'libswresample',
]

def mkArgs(platformPath, sdkPath, platformArgs)
	
	cc = '--cc=' + GCC_PATH
	as = ""
	sysroot = '--sysroot=' + XCODE_PATH + platformPath + sdkPath
#	extra = '--extra-ldflags=-L' + XCODE_PATH + platformPath + sdkPath + LIB_PATH
	extra = ""
	args = FFMPEG_BUILD_ARGS + platformArgs + MY_SELF_PLATFORM_ARGS
	args << cc 
	args << as
	args << sysroot
	args << extra
	
	args.join(' ')
end

def moveLibs(dest)
	FFMPEG_LIBS.each do |x|
		FileUtils.move Pathname.new("FFmpeg/#{x}/#{x}.a"), dest		
	end
end

def ensureDir(path)

	dest = Pathname.new path
	if dest.exist?
		FileUtils.rm Dir.glob("#{path}/*.a")
	else
		dest.mkpath
	end

	dest
end

def buildArch(arch)

	case arch
	when 'i386'
		args = mkArgs(PLATOFRM_PATH_SIM, SDK_PATH_SIM, FFMPEG_BUILD_ARGS_SIM)
    when 'x86_64'
		args = mkArgs(PLATOFRM_PATH_SIM, SDK_PATH_SIM, FFMPEG_BUILD_ARGS_SIM_64)
	when 'armv7'
		args = mkArgs(PLATOFRM_PATH_IOS, SDK_PATH_IOS, FFMPEG_BUILD_ARGS_ARMV7)
	when 'armv7s'
		args = mkArgs(PLATOFRM_PATH_IOS, SDK_PATH_IOS, FFMPEG_BUILD_ARGS_ARMV7S)		
	when 'arm64'
		args = mkArgs(PLATOFRM_PATH_IOS, SDK_PATH_IOS, FFMPEG_BUILD_ARGS_ARM64)		
	else
		raise "Build failed: unknown arch: #{arch}"
	end
	
	dest = ensureDir('FFmpeg/' + arch)
	
	system_or_exit "cd FFmpeg; ./configure #{args}"
	system_or_exit "cd FFmpeg; make"	
	moveLibs(dest)	
	system_or_exit "cd FFmpeg; [ -f -.d ] && rm -- -.d; make clean"

end

def mkLipoArgs(lib)
	"-create -arch armv7 armv7/#{lib}.a -arch armv7 armv7s/#{lib}.a -arch arm64 arm64/#{lib}.a -arch i386 i386/#{lib}.a -arch x86_64 x86_64/#{lib}.a -output universal/#{lib}.a"
end

desc "check gas-preprocessor.pl"
task :check_gas_preprocessor do	

	found = false

	ENV['PATH'].split(':').each do |x|
		p = Pathname.new(x) + 'gas-preprocessor.pl'
		if p.exist? && p.writable?
			found = true
			break;
		end
	end

	unless found
    # See http://stackoverflow.com/questions/5056600/how-to-install-gas-preprocessor for more info.
    puts "Installing the gas-preprocessor to #{GASPREP_DEST_PATH}"
    
    FileUtils.move Pathname.new("gas-preprocessor/gas-preprocessor.pl"), Pathname.new(GASPREP_DEST_PATH)
  	system_or_exit "chmod +x #{GASPREP_DEST_PATH}/gas-preprocessor.pl"
    
    # raise "Build failed: first install gas-preprocessor.pl.\nSee http://stackoverflow.com/questions/5056600/how-to-install-gas-preprocessor for more info."
	end

end

desc "Clean ffmpeg"
task :clean_ffmpeg do
	system_or_exit "cd FFmpeg; [ -f -.d ] && rm -- -.d; make clean"
end

desc "Build ffmpeg i386 libs"
task :build_ffmpeg_i386 do
	buildArch('i386')
end

desc "Build ffmpeg x86_64 libs"
task :build_ffmpeg_x86_64 do
buildArch('x86_64')
end

desc "Build ffmpeg armv7 libs"
task :build_ffmpeg_armv7 do
	buildArch('armv7')
end

desc "Build ffmpeg armv7s libs"
task :build_ffmpeg_armv7s do
	buildArch('armv7s')
end

desc "Build ffmpeg arm64 libs"
task :build_ffmpeg_arm64 do	
	buildArch('arm64')	
end

desc "Build ffmpeg universal libs"
task :build_ffmpeg_universal do	

	ensureDir('FFmpeg/universal')
	
	FFMPEG_LIBS.each do |x|
		args = mkLipoArgs(x)
		system_or_exit "cd FFmpeg; xcrun -sdk iphoneos lipo #{args}"
	end
	
	dest = ensureDir('libs/FFmpeg/')

	FFMPEG_LIBS.each do |x|
		FileUtils.move Pathname.new("FFmpeg/universal/#{x}.a"), dest
	end

end

## build libkxmovie

def cleanMovieLib(config)
	buildDir = Pathname.new 'tmp/build'	
  	system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration #{config} -sdk iphoneos#{SDK_VERSION} clean SYMROOT=#{buildDir}"
	system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration #{config} -sdk iphonesimulator#{SDK_VERSION} clean SYMROOT=#{buildDir}"  	
end

desc "Clean libkxmovie-debug"
task :clean_movie_debug do
	cleanMovieLib 'Debug'
end

desc "Clean libkxmovie-release"
task :clean_movie_release do
	cleanMovieLib 'Release'
end

desc "Build libkxmovie-debug"
task :build_movie_debug do
buildDir = Pathname.new 'tmp/build'
system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Debug -sdk iphoneos#{SDK_VERSION} build SYMROOT=#{buildDir} -arch armv7s"
FileUtils.move Pathname.new('tmp/build/Debug-iphoneos/libkxmovie.a'), Pathname.new('tmp/build/Debug-iphoneos/libkxmovie_armv7s.a')

system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Debug -sdk iphoneos#{SDK_VERSION} build SYMROOT=#{buildDir} -arch arm64"
FileUtils.move Pathname.new('tmp/build/Debug-iphoneos/libkxmovie.a'), Pathname.new('tmp/build/Debug-iphoneos/libkxmovie_arm64.a')

system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Debug -sdk iphoneos#{SDK_VERSION} build SYMROOT=#{buildDir} -arch armv7"

system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Debug -sdk iphonesimulator#{SDK_VERSION} build SYMROOT=#{buildDir} -arch x86_64"
FileUtils.move Pathname.new('tmp/build/Debug-iphonesimulator/libkxmovie.a'), Pathname.new('tmp/build/Debug-iphonesimulator/libkxmovie_x86_64.a')

system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Debug -sdk iphonesimulator#{SDK_VERSION} build SYMROOT=#{buildDir}"
system_or_exit "lipo -create -arch armv7 tmp/build/Debug-iphoneos/libkxmovie.a -arch armv7 tmp/build/Debug-iphoneos/libkxmovie_armv7s.a -arch arm64 tmp/build/Debug-iphoneos/libkxmovie_arm64.a -arch x86_64 tmp/build/Debug-iphonesimulator/libkxmovie_x86_64.a -arch i386 tmp/build/Debug-iphonesimulator/libkxmovie.a -output tmp/build/libkxmovie.a"
end

desc "Build libkxmovie-release"
task :build_movie_release do
buildDir = Pathname.new 'tmp/build'
system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Release -sdk iphoneos#{SDK_VERSION} build SYMROOT=#{buildDir} -arch armv7s"
FileUtils.move Pathname.new('tmp/build/Release-iphoneos/libkxmovie.a'), Pathname.new('tmp/build/Release-iphoneos/libkxmovie_armv7s.a')

system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Release -sdk iphoneos#{SDK_VERSION} build SYMROOT=#{buildDir} -arch arm64"
FileUtils.move Pathname.new('tmp/build/Release-iphoneos/libkxmovie.a'), Pathname.new('tmp/build/Release-iphoneos/libkxmovie_arm64.a')

system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Release -sdk iphoneos#{SDK_VERSION} build SYMROOT=#{buildDir} -arch armv7"

system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Debug -sdk iphonesimulator#{SDK_VERSION} build SYMROOT=#{buildDir} -arch x86_64"

FileUtils.move Pathname.new('tmp/build/Debug-iphonesimulator/libkxmovie.a'), Pathname.new('tmp/build/Debug-iphonesimulator/libkxmovie_x86_64.a')

system_or_exit "xcodebuild -project kxmovie.xcodeproj -target kxmovie -configuration Debug -sdk iphonesimulator#{SDK_VERSION} build SYMROOT=#{buildDir}"

system_or_exit "lipo -create -arch armv7 tmp/build/Release-iphoneos/libkxmovie.a -arch armv7 tmp/build/Release-iphoneos/libkxmovie_armv7s.a -arch arm64 tmp/build/Release-iphoneos/libkxmovie_arm64.a -arch x86_64 tmp/build/Debug-iphonesimulator/libkxmovie_x86_64.a -arch i386 tmp/build/Debug-iphonesimulator/libkxmovie.a -output tmp/build/libkxmovie.a"

#FileUtils.copy Pathname.new('tmp/build/Release-iphoneos/libkxmovie.a'), buildDir
end

desc "Copy to output folder"
task :copy_movie do	
	dest = ensureDir 'output'	
	FileUtils.move Pathname.new('tmp/build/libkxmovie.a'), dest		
	FileUtils.copy Pathname.new('libs/FFmpeg/libavcodec.a'), dest
	FileUtils.copy Pathname.new('libs/FFmpeg/libavformat.a'), dest
	FileUtils.copy Pathname.new('libs/FFmpeg/libavutil.a'), dest
	FileUtils.copy Pathname.new('libs/FFmpeg/libswscale.a'), dest
	FileUtils.copy Pathname.new('libs/FFmpeg/libswresample.a'), dest
	FileUtils.copy Pathname.new('kxmovie/KxMovieViewController.h'), dest	
	FileUtils.copy Pathname.new('kxmovie/KxAudioManager.h'), dest	
	FileUtils.copy Pathname.new('kxmovie/KxMovieDecoder.h'), dest
	FileUtils.copy_entry Pathname.new('kxmovie/kxmovie.bundle'), dest + 'kxmovie.bundle'
end	

##
task :clean => [:clean_movie_debug, :clean_movie_release, :clean_ffmpeg]
task :build_ffmpeg => [:check_gas_preprocessor, :build_ffmpeg_armv7, :build_ffmpeg_armv7s, :build_ffmpeg_arm64, :build_ffmpeg_i386, :build_ffmpeg_x86_64, :build_ffmpeg_universal]
#task :build_movie => [:build_movie_debug, :copy_movie] 
task :build_movie => [:build_movie_release, :copy_movie] 
task :build_all => [:build_ffmpeg, :build_movie] 
# task :default => [:build_all]
task :default => [:build_ffmpeg]

   此文件添加了 MY_SELF_PLATFORM_ARGS 自定義的編譯選項,僅僅支持播放flv和mp3文件(項目需要),因此可以大大縮小編譯後的文件大小。此外還定義了MIN_IOS_VERSION = ‘7.0’ ,希望對你有所幫助。


另外,將ffmpeg運用在你自己的工程裏邊的話,處理好頭文件路徑是非常重要和關鍵的步驟,切記!



最近公司需要做一個flv播放器,而且只要播放聲音即可。剛開始找到了Vitamio,發現用它來播放flv確實不怎麼樣,而且沒有iOS的源碼,感覺很不讓人放心。於是開始了編譯FFmpeg的歷程。話說Github上有一鍵編譯的各種腳本,但是難點不是編譯,而是如何設定參數來滿足公司的需求。要知道FFmpeg支持的編碼非常之多,而我僅僅關心flv。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章