Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / ffmpeg / chromium / scripts / build_ffmpeg.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2014 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 from __future__ import print_function
8
9 import collections
10 import multiprocessing
11 import optparse
12 import os
13 import platform
14 import re
15 import shutil
16 import subprocess
17 import sys
18
19
20 SCRIPTS_DIR = os.path.abspath(os.path.dirname(__file__))
21 FFMPEG_DIR = os.path.abspath(os.path.join(SCRIPTS_DIR, '..', '..'))
22
23
24 BRANDINGS = [
25   'Chrome',
26   'ChromeOS',
27   'Chromium',
28   'ChromiumOS',
29 ]
30
31
32 USAGE = """Usage: %prog TARGET_OS TARGET_ARCH [options] -- [configure_args]
33
34 Valid combinations are linux       [ia32|x64|mipsel|arm|arm-neon]
35                        linux-noasm [x64]
36                        mac         [ia32|x64]
37                        win         [ia32|x64]
38
39 Platform specific build notes:
40   linux ia32/x64:
41     Script can run on a normal Ubuntu box.
42
43   linux mipsel:
44     Script can run on a normal Ubuntu box with MIPS cross-toolchain in $PATH.
45
46   linux arm/arm-neon:
47     Script must be run inside of ChromeOS chroot with BOARD=arm-generic.
48
49   mac:
50     Script must be run on OSX.  Additionally, ensure the Chromium (not Apple)
51     version of clang is in the path; usually found under
52     src/third_party/llvm-build/Release+Asserts/bin
53
54   win:
55     Script must be run on Windows with VS2013 or higher under Cygwin (or MinGW,
56     but as of 1.0.11, it has serious performance issues with make which makes
57     building take hours).
58
59     Additionall, ensure you have the correct toolchain environment for building.
60     The x86 toolchain environment is required for ia32 builds and the x64 one
61     for x64 builds.  This can be verified by running "cl.exe" and checking if
62     the version string ends with "for x64" or "for x86."
63
64     Building on Windows also requires some additional Cygwin packages plus a
65     wrapper script for converting Cygwin paths to DOS paths.
66       - Add these packages at install time: diffutils, yasm, make, python.
67       - Copy chromium/scripts/cygwin-wrapper to /usr/local/bin
68
69 Resulting binaries will be placed in:
70   build.TARGET_ARCH.TARGET_OS/Chromium/out/
71   build.TARGET_ARCH.TARGET_OS/Chrome/out/
72   build.TARGET_ARCH.TARGET_OS/ChromiumOS/out/
73   build.TARGET_ARCH.TARGET_OS/ChromeOS/out/"""
74
75
76 def PrintAndCheckCall(argv, *args, **kwargs):
77   print('Running %r' % argv)
78   subprocess.check_call(argv, *args, **kwargs)
79
80
81 def DetermineHostOsAndArch():
82   if platform.system() == 'Linux':
83     host_os = 'linux'
84   elif platform.system() == 'Darwin':
85     host_os = 'mac'
86   elif platform.system() == 'Windows' or platform.system() == 'CYGWIN_NT-6.1':
87     host_os = 'win'
88   else:
89     return None
90
91   if re.match(r'i.86', platform.machine()):
92     host_arch = 'ia32'
93   elif platform.machine() == 'x86_64' or platform.machine() == 'AMD64':
94     host_arch = 'x64'
95   elif platform.machine().startswith('arm'):
96     host_arch = 'arm'
97   else:
98     return None
99
100   return (host_os, host_arch)
101
102
103 def GetDsoName(target_os, dso_name, dso_version):
104   if target_os == 'linux':
105     return 'lib%s.so.%s' % (dso_name, dso_version)
106   elif target_os == 'mac':
107     return 'lib%s.%s.dylib' % (dso_name, dso_version)
108   elif target_os == 'win':
109     return '%s-%s.dll' % (dso_name, dso_version)
110   else:
111     raise ValueError('Unexpected target_os %s' % target_os)
112
113
114 def RewriteFile(path, search, replace):
115   with open(path) as f:
116     contents = f.read()
117   with open(path, 'w') as f:
118     f.write(re.sub(search, replace, contents))
119
120
121 def BuildFFmpeg(target_os, target_arch, host_os, host_arch, parallel_jobs,
122                 config_only, config, configure_flags):
123   config_dir = 'build.%s.%s/%s' % (target_arch, target_os, config)
124   shutil.rmtree(config_dir, ignore_errors=True)
125   os.makedirs(os.path.join(config_dir, 'out'))
126
127   PrintAndCheckCall(
128       [os.path.join(FFMPEG_DIR, 'configure')] + configure_flags, cwd=config_dir)
129
130   if (target_os, target_arch) == ('mac', 'ia32'):
131     # Required to get Mac ia32 builds compiling with -fno-omit-frame-pointer,
132     # which is required for accurate stack traces.  See http://crbug.com/115170.
133     #
134     # Without this, building without -fomit-frame-pointer on ia32 will result in
135     # the the inclusion of a number of inline assembly blocks that use too many
136     # registers for its input/output operands.
137     for name in ('config.h', 'config.asm'):
138       RewriteFile(os.path.join(config_dir, name),
139                   'HAVE_EBP_AVAILABLE 1',
140                   'HAVE_EBP_AVAILABLE 0')
141
142   if host_os == target_os and not config_only:
143     libraries = [
144         os.path.join('libavcodec', GetDsoName(target_os, 'avcodec', 56)),
145         os.path.join('libavformat', GetDsoName(target_os, 'avformat', 56)),
146         os.path.join('libavutil', GetDsoName(target_os, 'avutil', 54)),
147     ]
148     PrintAndCheckCall(
149         ['make', '-j%d' % parallel_jobs] + libraries, cwd=config_dir)
150     for lib in libraries:
151       shutil.copy(os.path.join(config_dir, lib),
152                   os.path.join(config_dir, 'out'))
153   elif config_only:
154     print('Skipping build step as requested.')
155   else:
156     print('Skipping compile as host configuration differs from target.\n'
157           'Please compare the generated config.h with the previous version.\n'
158           'You may also patch the script to properly cross-compile.\n'
159           'Host OS : %s\n'
160           'Target OS : %s\n'
161           'Host arch : %s\n'
162           'Target arch : %s\n' % (host_os, target_os, host_arch, target_arch))
163
164   if target_arch in ('arm', 'arm-neon'):
165     RewriteFile(
166         os.path.join(config_dir, 'config.h'),
167         r'(#define HAVE_VFP_ARGS [01])',
168         r'/* \1 -- Disabled to allow softfp/hardfp selection at gyp time */')
169
170
171 def main(argv):
172   parser = optparse.OptionParser(usage=USAGE)
173   parser.add_option('--branding', action='append', dest='brandings',
174                     choices=BRANDINGS,
175                     help='Branding to build; determines e.g. supported codecs')
176   parser.add_option('--config-only', action='store_true',
177                     help='Skip the build step. Useful when a given platform '
178                     'is not necessary for generate_gyp.py')
179   options, args = parser.parse_args(argv)
180
181   if len(args) < 2:
182     parser.print_help()
183     return 1
184
185   target_os = args[0]
186   target_arch = args[1]
187   configure_args = args[2:]
188
189   if target_os not in ('linux', 'linux-noasm', 'win', 'mac'):
190     parser.print_help()
191     return 1
192
193   host_tuple = DetermineHostOsAndArch()
194   if not host_tuple:
195     print('Unrecognized host OS and architecture.', file=sys.stderr)
196     return 1
197
198   host_os, host_arch = host_tuple
199   parallel_jobs = multiprocessing.cpu_count()
200
201   print('System information:\n'
202         'Host OS       : %s\n'
203         'Target OS     : %s\n'
204         'Host arch     : %s\n'
205         'Target arch   : %s\n'
206         'Parallel jobs : %d\n' % (
207             host_os, target_os, host_arch, target_arch, parallel_jobs))
208
209   configure_flags = collections.defaultdict(list)
210
211   # Common configuration.  Note: --disable-everything does not in fact disable
212   # everything, just non-library components such as decoders and demuxers.
213   configure_flags['Common'].extend([
214       '--disable-everything',
215       '--disable-all',
216       '--disable-doc',
217       '--disable-static',
218       '--enable-avcodec',
219       '--enable-avformat',
220       '--enable-avutil',
221       '--enable-fft',
222       '--enable-rdft',
223       '--enable-shared',
224
225       # Disable features.
226       '--disable-bzlib',
227       '--disable-error-resilience',
228       '--disable-iconv',
229       '--disable-lzo',
230       '--disable-network',
231       '--disable-symver',
232       '--disable-xlib',
233       '--disable-zlib',
234
235       # Disable hardware decoding options which will sometimes turn on
236       # via autodetect.
237       '--disable-dxva2',
238       '--disable-vaapi',
239       '--disable-vda',
240       '--disable-vdpau',
241
242       # Common codecs.
243       '--enable-decoder=theora,vorbis,vp8',
244       '--enable-decoder=pcm_u8,pcm_s16le,pcm_s24le,pcm_f32le',
245       '--enable-decoder=pcm_s16be,pcm_s24be,pcm_mulaw,pcm_alaw',
246       '--enable-demuxer=ogg,matroska,wav',
247       '--enable-parser=opus,vp3,vorbis,vp8',
248   ])
249
250   # --optflags doesn't append multiple entries, so set all at once.
251   if (target_os, target_arch) == ('mac', 'ia32'):
252     configure_flags['Common'].append('--optflags="-fno-omit-frame-pointer -O2"')
253   else:
254     configure_flags['Common'].append('--optflags="-O2"')
255
256   # Linux only.
257   if target_os in ('linux', 'linux-noasm'):
258     if target_arch == 'x64':
259       pass
260     elif target_arch == 'ia32':
261       configure_flags['Common'].extend([
262           '--arch=i686',
263           '--enable-yasm',
264           '--extra-cflags="-m32"',
265           '--extra-ldflags="-m32"',
266       ])
267     elif target_arch == 'arm':
268       if host_arch != 'arm':
269         configure_flags['Common'].extend([
270             # This if-statement essentially is for chroot tegra2.
271             '--enable-cross-compile',
272
273             # Location is for CrOS chroot. If you want to use this, enter chroot
274             # and copy ffmpeg to a location that is reachable.
275             '--cross-prefix=/usr/bin/armv7a-cros-linux-gnueabi-',
276             '--target-os=linux',
277             '--arch=arm',
278         ])
279
280       # TODO(ihf): ARM compile flags are tricky. The final options
281       # overriding everything live in chroot /build/*/etc/make.conf
282       # (some of them coming from src/overlays/overlay-<BOARD>/make.conf).
283       # We try to follow these here closely. In particular we need to
284       # set ffmpeg internal #defines to conform to make.conf.
285       # TODO(ihf): For now it is not clear if thumb or arm settings would be
286       # faster. I ran experiments in other contexts and performance seemed
287       # to be close and compiler version dependent. In practice thumb builds are
288       # much smaller than optimized arm builds, hence we go with the global
289       # CrOS settings.
290       configure_flags['Common'].extend([
291           '--enable-armv6',
292           '--enable-armv6t2',
293           '--enable-vfp',
294           '--enable-thumb',
295           '--disable-neon',
296           '--extra-cflags=-march=armv7-a',
297           '--extra-cflags=-mtune=cortex-a8',
298           '--extra-cflags=-mfpu=vfpv3-d16',
299           # NOTE: softfp/hardfp selected at gyp time.
300           '--extra-cflags=-mfloat-abi=hard',
301       ])
302     elif target_arch == 'arm-neon':
303       if host_arch != 'arm':
304         # This if-statement is for chroot arm-generic.
305         configure_flags['Common'].extend([
306             '--enable-cross-compile',
307             '--cross-prefix=/usr/bin/armv7a-cros-linux-gnueabi-',
308             '--target-os=linux',
309             '--arch=arm',
310         ])
311       configure_flags['Common'].extend([
312           '--enable-armv6',
313           '--enable-armv6t2',
314           '--enable-vfp',
315           '--enable-thumb',
316           '--enable-neon',
317           '--extra-cflags=-march=armv7-a',
318           '--extra-cflags=-mtune=cortex-a8',
319           '--extra-cflags=-mfpu=neon',
320           # NOTE: softfp/hardfp selected at gyp time.
321           '--extra-cflags=-mfloat-abi=hard',
322       ])
323     elif target_arch == 'mipsel':
324       configure_flags['Common'].extend([
325           '--enable-cross-compile',
326           '--cross-prefix=mips-linux-gnu-',
327           '--target-os=linux',
328           '--arch=mips',
329           '--extra-cflags=-mips32',
330           '--extra-cflags=-EL',
331           '--extra-ldflags=-mips32',
332           '--extra-ldflags=-EL',
333           '--disable-mipsfpu',
334           '--disable-mipsdspr1',
335           '--disable-mipsdspr2',
336       ])
337     else:
338       print('Error: Unknown target arch %r for target OS %r!' % (
339           target_arch, target_os), file=sys.stderr)
340       return 1
341
342   if target_os == 'linux-noasm':
343     configure_flags['Common'].extend([
344         '--disable-asm',
345         '--disable-inline-asm',
346     ])
347
348   if 'win' not in target_os:
349     configure_flags['Common'].append('--enable-pic')
350
351   # Should be run on Mac.
352   if target_os == 'mac':
353     if host_os != 'mac':
354       print('Script should be run on a Mac host. If this is not possible\n'
355             'try a merge of config files with new linux ia32 config.h\n'
356             'by hand.\n', file=sys.stderr)
357       return 1
358
359     configure_flags['Common'].extend([
360         '--enable-yasm',
361         '--cc=clang',
362         '--cxx=clang++',
363     ])
364     if target_arch == 'ia32':
365       configure_flags['Common'].extend([
366           '--arch=i686',
367           '--extra-cflags=-m32',
368           '--extra-ldflags=-m32',
369       ])
370     elif target_arch == 'x64':
371       configure_flags['Common'].extend([
372           '--arch=x86_64',
373           '--extra-cflags=-m64',
374           '--extra-ldflags=-m64',
375       ])
376     else:
377       print('Error: Unknown target arch %r for target OS %r!' % (
378           target_arch, target_os), file=sys.stderr)
379
380   # Should be run on Windows.
381   if target_os == 'win':
382     if host_os != 'win':
383       print('Script should be run on a Windows host.\n', file=sys.stderr)
384       return 1
385
386     configure_flags['Common'].extend([
387         '--toolchain=msvc',
388         '--enable-yasm',
389         '--extra-cflags=-I' + os.path.join(FFMPEG_DIR, 'chromium/include/win'),
390     ])
391
392     if platform.system() == 'CYGWIN_NT-6.1':
393       configure_flags['Common'].extend([
394           '--cc=cygwin-wrapper cl',
395           '--ld=cygwin-wrapper link',
396           '--nm=cygwin-wrapper dumpbin -symbols',
397           '--ar=cygwin-wrapper lib',
398       ])
399
400   # Google Chrome & ChromeOS specific configuration.
401   configure_flags['Chrome'].extend([
402       '--enable-decoder=aac,h264,mp3',
403       '--enable-demuxer=aac,mp3,mov',
404       '--enable-parser=aac,h264,mpegaudio',
405   ])
406
407   # ChromiumOS specific configuration.
408   # Warning: do *NOT* add avi, aac, h264, mp3, mp4, amr*
409   # Flac support.
410   configure_flags['ChromiumOS'].extend([
411       '--enable-demuxer=flac',
412       '--enable-decoder=flac',
413       '--enable-parser=flac',
414   ])
415
416   # Google ChromeOS specific configuration.
417   # We want to make sure to play everything Android generates and plays.
418   # http://developer.android.com/guide/appendix/media-formats.html
419   configure_flags['ChromeOS'].extend([
420       # Enable playing avi files.
421       '--enable-decoder=mpeg4',
422       '--enable-parser=h263,mpeg4video',
423       '--enable-demuxer=avi',
424       # Enable playing Android 3gp files.
425       '--enable-demuxer=amr',
426       '--enable-decoder=amrnb,amrwb',
427       # Flac support.
428       '--enable-demuxer=flac',
429       '--enable-decoder=flac',
430       '--enable-parser=flac',
431       # Wav files for playing phone messages.
432       '--enable-decoder=gsm_ms',
433       '--enable-demuxer=gsm',
434       '--enable-parser=gsm',
435   ])
436
437   def do_build_ffmpeg(branding, configure_flags):
438     if options.brandings and branding not in options.brandings:
439       print('%s skipped' % branding)
440       return
441
442     print('%s configure/build:' % branding)
443     BuildFFmpeg(target_os, target_arch, host_os, host_arch, parallel_jobs,
444                 options.config_only, branding, configure_flags)
445
446   do_build_ffmpeg('Chromium',
447                   configure_flags['Common'] +
448                   configure_flags['Chromium'] +
449                   configure_args)
450   do_build_ffmpeg('Chrome',
451                   configure_flags['Common'] +
452                   configure_flags['Chrome'] +
453                   configure_args)
454
455   if target_os == 'linux':
456     do_build_ffmpeg('ChromiumOS',
457                     configure_flags['Common'] +
458                     configure_flags['Chromium'] +
459                     configure_flags['ChromiumOS'] +
460                     configure_args)
461
462     # ChromeOS enables MPEG4 which requires error resilience :(
463     chrome_os_flags = (configure_flags['Common'] +
464                        configure_flags['Chrome'] +
465                        configure_flags['ChromeOS'] +
466                        configure_args)
467     chrome_os_flags.remove('--disable-error-resilience')
468     do_build_ffmpeg('ChromeOS', chrome_os_flags)
469
470   print('Done. If desired you may copy config.h/config.asm into the '
471         'source/config tree using copy_config.sh.')
472   return 0
473
474
475 if __name__ == '__main__':
476   sys.exit(main(sys.argv[1:]))