树莓派移植FFmepg记录(x264、硬件编码支持)

树莓派移植FFmepg记录(x264、硬件编码支持)

参考链接:

  • 树莓派编译安装FFmpeg(添加H.264硬件编解码器支持)_唐传林的博客-CSDN博客
  • ubuntu下交叉编译X264和FFMPEG到RK3399平台(编译器:aarch64-linux-gcc)-阿里云开发者社区 (aliyun.com)
  • FFmpeg encode_video.c示例 - 简书 (jianshu.com)

本次移植为FFmpeg添加了x264软件编码器、Omx-rpi硬件编码的支持

1.x264交叉编译

进入源码目录,根据configure --help,编写一个脚本:

#!/bin/sh

./configure --prefix=./pi_install \
--enable-strip \
--disable-asm \
--host=arm-linux \
--cross-prefix=arm-linux-gnueabihf- \
--sysroot=/opt/rpi_tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot \
--enable-shared \
# --disable-cli #配置该项不会生成可执行文件

执行后会生成makefile,执行编译命令:

make 或make -j8 #多线程编译,提高编译速度

编译完成后使用make install命令则会将生成的文件拷贝到安装目录:

boy@ubuntu:~/opensource/x264/pi_install$ tree .
.
├── bin
│   └── x264
├── include
│   ├── x264_config.h
│   └── x264.h
└── lib
    ├── libx264.so -> libx264.so.164
    ├── libx264.so.164
    └── pkgconfig
        └── x264.pc

4 directories, 6 files
boy@ubuntu:~/opensource/x264/pi_install$

可以看到已经根据我们的configure配置项,生成了so动态库、可执行文件等。

2.ffmpeg交叉编译

源码下载链接:Download FFmpeg

解压:

tar -jxvf ffmpeg-snapshot.tar.bz2

与配置x264类似,使用configure --help可以查看到有很多选项配置,这里使用参考前人的配置编写一个合适的脚本:

#!/bin/sh

./configure \
--enable-shared \
--enable-static \
--prefix=`pwd`/pi_install \
--extra-cflags=-I/home/boy/opensource/x264/pi_install/include \ #添加外部头文件路径
--extra-ldflags=-L/home/boy/opensource/x264/pi_install/lib \    #添加外部库文件路径
--enable-libx264 #使能x264编码器
--enable-cross-compile \
--cross-prefix=arm-linux-gnueabihf- \
--arch=arm \
--target-os=linux \
--enable-gpl \

**注:**这些选项貌似会有一定的先后顺序,刚开始–extra-cflags、–extra-ldflags放在后面一直编译不成功。。

生成makefile后执行make -j8进行编译,代码比较多,编译会比较慢,编译完成后make install安装到相应路径:

.
├── bin
│   ├── ffmpeg
│   └── ffprobe
├── include
│   ├── libavcodec
│   │   ├── ac3_parser.h
│   │   ├── adts_parser.h
│   │   ├── avcodec.h
│   │   ├── avdct.h
│   │   ├── avfft.h
│   │   ├── bsf.h
│   │   ├── codec_desc.h
│   │   ├── codec.h
│   │   ├── codec_id.h
│   │   ├── codec_par.h
│   │   ├── d3d11va.h
│   │   ├── defs.h
│   │   ├── dirac.h
│   │   ├── dv_profile.h
│   │   ├── dxva2.h
│   │   ├── jni.h
│   │   ├── mediacodec.h
│   │   ├── packet.h
│   │   ├── qsv.h
│   │   ├── vdpau.h
│   │   ├── version.h
│   │   ├── version_major.h
│   │   ├── videotoolbox.h
│   │   ├── vorbis_parser.h
│   │   └── xvmc.h
│   ├── libavdevice
│   │   ├── avdevice.h
│   │   ├── version.h
│   │   └── version_major.h
│   ├── libavfilter
│   │   ├── avfilter.h
│   │   ├── buffersink.h
│   │   ├── buffersrc.h
│   │   ├── version.h
│   │   └── version_major.h
│   ├── libavformat
│   │   ├── avformat.h
│   │   ├── avio.h
│   │   ├── version.h
│   │   └── version_major.h
│   ├── libavutil
│   │   ├── adler32.h
│   │   ├── aes_ctr.h
│   │   ├── aes.h
│   │   ├── attributes.h
│   │   ├── audio_fifo.h
│   │   ├── avassert.h
│   │   ├── avconfig.h
│   │   ├── avstring.h
│   │   ├── avutil.h
│   │   ├── base64.h
│   │   ├── blowfish.h
│   │   ├── bprint.h
│   │   ├── bswap.h
│   │   ├── buffer.h
│   │   ├── camellia.h
│   │   ├── cast5.h
│   │   ├── channel_layout.h
│   │   ├── common.h
│   │   ├── cpu.h
│   │   ├── crc.h
│   │   ├── des.h
│   │   ├── detection_bbox.h
│   │   ├── dict.h
│   │   ├── display.h
│   │   ├── dovi_meta.h
│   │   ├── downmix_info.h
│   │   ├── encryption_info.h
│   │   ├── error.h
│   │   ├── eval.h
│   │   ├── ffversion.h
│   │   ├── fifo.h
│   │   ├── file.h
│   │   ├── film_grain_params.h
│   │   ├── frame.h
│   │   ├── hash.h
│   │   ├── hdr_dynamic_metadata.h
│   │   ├── hdr_dynamic_vivid_metadata.h
│   │   ├── hmac.h
│   │   ├── hwcontext_cuda.h
│   │   ├── hwcontext_d3d11va.h
│   │   ├── hwcontext_drm.h
│   │   ├── hwcontext_dxva2.h
│   │   ├── hwcontext.h
│   │   ├── hwcontext_mediacodec.h
│   │   ├── hwcontext_opencl.h
│   │   ├── hwcontext_qsv.h
│   │   ├── hwcontext_vaapi.h
│   │   ├── hwcontext_vdpau.h
│   │   ├── hwcontext_videotoolbox.h
│   │   ├── hwcontext_vulkan.h
│   │   ├── imgutils.h
│   │   ├── intfloat.h
│   │   ├── intreadwrite.h
│   │   ├── lfg.h
│   │   ├── log.h
│   │   ├── lzo.h
│   │   ├── macros.h
│   │   ├── mastering_display_metadata.h
│   │   ├── mathematics.h
│   │   ├── md5.h
│   │   ├── mem.h
│   │   ├── motion_vector.h
│   │   ├── murmur3.h
│   │   ├── opt.h
│   │   ├── parseutils.h
│   │   ├── pixdesc.h
│   │   ├── pixelutils.h
│   │   ├── pixfmt.h
│   │   ├── random_seed.h
│   │   ├── rational.h
│   │   ├── rc4.h
│   │   ├── replaygain.h
│   │   ├── ripemd.h
│   │   ├── samplefmt.h
│   │   ├── sha512.h
│   │   ├── sha.h
│   │   ├── spherical.h
│   │   ├── stereo3d.h
│   │   ├── tea.h
│   │   ├── threadmessage.h
│   │   ├── timecode.h
│   │   ├── time.h
│   │   ├── timestamp.h
│   │   ├── tree.h
│   │   ├── twofish.h
│   │   ├── tx.h
│   │   ├── version.h
│   │   ├── video_enc_params.h
│   │   └── xtea.h
│   ├── libpostproc
│   │   ├── postprocess.h
│   │   ├── version.h
│   │   └── version_major.h
│   ├── libswresample
│   │   ├── swresample.h
│   │   ├── version.h
│   │   └── version_major.h
│   └── libswscale
│       ├── swscale.h
│       ├── version.h
│       └── version_major.h
├── lib
│   ├── libavcodec.a
│   ├── libavcodec.so -> libavcodec.so.59.25.100
│   ├── libavcodec.so.59 -> libavcodec.so.59.25.100
│   ├── libavcodec.so.59.25.100
│   ├── libavdevice.a
│   ├── libavdevice.so -> libavdevice.so.59.6.100
│   ├── libavdevice.so.59 -> libavdevice.so.59.6.100
│   ├── libavdevice.so.59.6.100
│   ├── libavfilter.a
│   ├── libavfilter.so -> libavfilter.so.8.29.100
│   ├── libavfilter.so.8 -> libavfilter.so.8.29.100
│   ├── libavfilter.so.8.29.100
│   ├── libavformat.a
│   ├── libavformat.so -> libavformat.so.59.20.101
│   ├── libavformat.so.59 -> libavformat.so.59.20.101
│   ├── libavformat.so.59.20.101
│   ├── libavutil.a
│   ├── libavutil.so -> libavutil.so.57.24.101
│   ├── libavutil.so.57 -> libavutil.so.57.24.101
│   ├── libavutil.so.57.24.101
│   ├── libpostproc.a
│   ├── libpostproc.so -> libpostproc.so.56.5.100
│   ├── libpostproc.so.56 -> libpostproc.so.56.5.100
│   ├── libpostproc.so.56.5.100
│   ├── libswresample.a
│   ├── libswresample.so -> libswresample.so.4.6.100
│   ├── libswresample.so.4 -> libswresample.so.4.6.100
│   ├── libswresample.so.4.6.100
│   ├── libswscale.a
│   ├── libswscale.so -> libswscale.so.6.6.100
│   ├── libswscale.so.6 -> libswscale.so.6.6.100
│   ├── libswscale.so.6.6.100
│   └── pkgconfig
│       ├── libavcodec.pc
│       ├── libavdevice.pc
│       ├── libavfilter.pc
│       ├── libavformat.pc
│       ├── libavutil.pc
│       ├── libpostproc.pc
│       ├── libswresample.pc
│       └── libswscale.pc
├── share
│   ├── ffmpeg
│   │   ├── examples
│   │   │   ├── avio_list_dir.c
│   │   │   ├── avio_reading.c
│   │   │   ├── decode_audio.c
│   │   │   ├── decode_video.c
│   │   │   ├── demuxing_decoding.c
│   │   │   ├── encode_audio.c
│   │   │   ├── encode_video.c
│   │   │   ├── extract_mvs.c
│   │   │   ├── filter_audio.c
│   │   │   ├── filtering_audio.c
│   │   │   ├── filtering_video.c
│   │   │   ├── http_multiclient.c
│   │   │   ├── hw_decode.c
│   │   │   ├── Makefile
│   │   │   ├── metadata.c
│   │   │   ├── muxing.c
│   │   │   ├── qsvdec.c
│   │   │   ├── README
│   │   │   ├── remuxing.c
│   │   │   ├── resampling_audio.c
│   │   │   ├── scaling_video.c
│   │   │   ├── transcode_aac.c
│   │   │   ├── transcoding.c
│   │   │   ├── vaapi_encode.c
│   │   │   └── vaapi_transcode.c
│   │   ├── ffprobe.xsd
│   │   ├── libvpx-1080p50_60.ffpreset
│   │   ├── libvpx-1080p.ffpreset
│   │   ├── libvpx-360p.ffpreset
│   │   ├── libvpx-720p50_60.ffpreset
│   │   └── libvpx-720p.ffpreset
│   └── man
│       ├── man1
│       │   ├── ffmpeg.1
│       │   ├── ffmpeg-all.1
│       │   ├── ffmpeg-bitstream-filters.1
│       │   ├── ffmpeg-codecs.1
│       │   ├── ffmpeg-devices.1
│       │   ├── ffmpeg-filters.1
│       │   ├── ffmpeg-formats.1
│       │   ├── ffmpeg-protocols.1
│       │   ├── ffmpeg-resampler.1
│       │   ├── ffmpeg-scaler.1
│       │   ├── ffmpeg-utils.1
│       │   ├── ffprobe.1
│       │   └── ffprobe-all.1
│       └── man3
│           ├── libavcodec.3
│           ├── libavdevice.3
│           ├── libavfilter.3
│           ├── libavformat.3
│           ├── libavutil.3
│           ├── libswresample.3
│           └── libswscale.3
└── tree.txt

18 directories, 229 files

编译完成即可拷贝头文件、库文件到自己的工程目录,编写相关应用程序。

3.添加硬件编码支持

​ libx264是使用CPU进行编码转换的,即软件编码,运行起来CPU占用率非常高。为了降低CPU占用率,决定使用硬件加速编码。

首先,需要知道两个比较关键的东西:

  • omx-rpi 树莓派的硬编码加速
  • mmal 树莓派的硬解码加速

PS:博主只用到编码,因此只添加硬件编码支持,硬件解码的添加应该大同小异,有空再折腾。

​ 树莓派交叉编译添加硬件编码支持的资料非常少,折腾了非常久( ~ . ~痛苦面具),最后在某歌搜索到一个比较有用的博客:为树莓派交叉编译FFMPEG工具 | Sunrise 博客 (yjdwbj.github.io)

①.获取树莓派omx库、头文件

根据该博客的指导,在树莓派/opt/vc目录下包含有omxmmal相关的库文件、头文件以及相关的demo,如下所示,将这些拷贝到ubuntu宿主机。

boy@ubuntu:~/opensource/vc$ tree .
.
├── bin
│   ├── containers_check_frame_int
│   ├── containers_datagram_receiver
│   ├── containers_datagram_sender
│   ├── containers_dump_pktfile
│   ├── containers_rtp_decoder
│   ├── containers_stream_client
│   ├── containers_stream_server
│   ├── containers_test
│   ├── containers_test_bits
│   ├── containers_test_uri
│   ├── containers_uri_pipe
│   ├── dtmerge
│   ├── dtoverlay
│   ├── dtoverlay-post
│   ├── dtoverlay-pre
│   ├── dtparam -> dtoverlay
│   ├── edidparser
│   ├── mmal_vc_diag
│   ├── raspistill
│   ├── raspivid
│   ├── raspividyuv
│   ├── raspiyuv
│   ├── tvservice
│   ├── vcdbg
│   ├── vcgencmd
│   ├── vchiq_test
│   ├── vcmailbox
│   └── vcsmem
├── include
│   ├── bcm_host.h
│   ├── EGL
│   │   ├── eglext_android.h
│   │   ├── eglext_brcm.h
│   │   ├── eglext.h
│   │   ├── eglext_nvidia.h
│   │   ├── egl.h
│   │   └── eglplatform.h
│   ├── GLES
│   │   ├── glext.h
│   │   ├── gl.h
│   │   └── glplatform.h
│   ├── GLES2
│   │   ├── gl2ext.h
│   │   ├── gl2.h
│   │   └── gl2platform.h
│   ├── IL
│   │   ├── OMX_Audio.h
│   │   ├── OMX_Broadcom.h
│   │   ├── OMX_Component.h
│   │   ├── OMX_Core.h
│   │   ├── OMX_ILCS.h
│   │   ├── OMX_Image.h
│   │   ├── OMX_Index.h
│   │   ├── OMX_IVCommon.h
│   │   ├── OMX_Other.h
│   │   ├── OMX_Types.h
│   │   └── OMX_Video.h
│   ├── interface
│   │   ├── debug_sym
│   │   │   └── debug_sym.h
│   │   ├── mmal
│   │   │   ├── core
│   │   │   │   ├── mmal_buffer_private.h
│   │   │   │   ├── mmal_clock_private.h
│   │   │   │   ├── mmal_component_private.h
│   │   │   │   ├── mmal_core_private.h
│   │   │   │   ├── mmal_events_private.h
│   │   │   │   └── mmal_port_private.h
│   │   │   ├── mmal_buffer.h
│   │   │   ├── mmal_clock.h
│   │   │   ├── mmal_common.h
│   │   │   ├── mmal_component.h
│   │   │   ├── mmal_encodings.h
│   │   │   ├── mmal_events.h
│   │   │   ├── mmal_format.h
│   │   │   ├── mmal.h
│   │   │   ├── mmal_logging.h
│   │   │   ├── mmal_parameters_audio.h
│   │   │   ├── mmal_parameters_camera.h
│   │   │   ├── mmal_parameters_clock.h
│   │   │   ├── mmal_parameters_common.h
│   │   │   ├── mmal_parameters.h
│   │   │   ├── mmal_parameters_video.h
│   │   │   ├── mmal_pool.h
│   │   │   ├── mmal_port.h
│   │   │   ├── mmal_queue.h
│   │   │   ├── mmal_types.h
│   │   │   ├── util
│   │   │   │   ├── mmal_component_wrapper.h
│   │   │   │   ├── mmal_connection.h
│   │   │   │   ├── mmal_default_components.h
│   │   │   │   ├── mmal_graph.h
│   │   │   │   ├── mmal_il.h
│   │   │   │   ├── mmal_list.h
│   │   │   │   ├── mmal_param_convert.h
│   │   │   │   ├── mmal_util.h
│   │   │   │   ├── mmal_util_params.h
│   │   │   │   └── mmal_util_rational.h
│   │   │   └── vc
│   │   │       ├── mmal_vc_api_drm.h
│   │   │       ├── mmal_vc_api.h
│   │   │       ├── mmal_vc_client_priv.h
│   │   │       ├── mmal_vc_msgnames.h
│   │   │       ├── mmal_vc_msgs.h
│   │   │       ├── mmal_vc_opaque_alloc.h
│   │   │       └── mmal_vc_shm.h
│   │   ├── peer
│   │   │   └── vc_vchi_dispmanx_common.h
│   │   ├── vchi
│   │   │   ├── common
│   │   │   │   └── endian.h
│   │   │   ├── connections
│   │   │   │   └── connection.h
│   │   │   ├── message_drivers
│   │   │   │   └── message.h
│   │   │   ├── vchi_cfg.h
│   │   │   ├── vchi_cfg_internal.h
│   │   │   ├── vchi_common.h
│   │   │   ├── vchi.h
│   │   │   └── vchi_mh.h
│   │   ├── vchiq_arm
│   │   │   ├── vchiq_cfg.h
│   │   │   ├── vchiq.h
│   │   │   ├── vchiq_if.h
│   │   │   ├── vchiq_ioctl.h
│   │   │   ├── vchiq_test.h
│   │   │   ├── vchiq_test_if.h
│   │   │   └── vchiq_util.h
│   │   ├── vcos
│   │   │   ├── generic
│   │   │   │   ├── vcos_common.h
│   │   │   │   ├── vcos_deprecated.h
│   │   │   │   ├── vcos_generic_blockpool.h
│   │   │   │   ├── vcos_generic_event_flags.h
│   │   │   │   ├── vcos_generic_named_sem.h
│   │   │   │   ├── vcos_generic_quickslow_mutex.h
│   │   │   │   ├── vcos_generic_reentrant_mtx.h
│   │   │   │   ├── vcos_generic_tls.h
│   │   │   │   ├── vcos_joinable_thread_from_plain.h
│   │   │   │   ├── vcos_latch_from_sem.h
│   │   │   │   ├── vcos_mem_from_malloc.h
│   │   │   │   ├── vcos_mutexes_are_reentrant.h
│   │   │   │   └── vcos_thread_reaper.h
│   │   │   ├── pthreads
│   │   │   │   ├── vcos_futex_mutex.h
│   │   │   │   ├── vcos_platform.h
│   │   │   │   └── vcos_platform_types.h
│   │   │   ├── user_nodefs.h
│   │   │   ├── vcos_assert.h
│   │   │   ├── vcos_atomic_flags.h
│   │   │   ├── vcos_attr.h
│   │   │   ├── vcos_blockpool.h
│   │   │   ├── vcos_build_info.h
│   │   │   ├── vcos_cfg.h
│   │   │   ├── vcos_cmd.h
│   │   │   ├── vcos_ctype.h
│   │   │   ├── vcos_dlfcn.h
│   │   │   ├── vcos_event_flags.h
│   │   │   ├── vcos_event.h
│   │   │   ├── vcos.h
│   │   │   ├── vcos_init.h
│   │   │   ├── vcos_inttypes.h
│   │   │   ├── vcos_isr.h
│   │   │   ├── vcos_legacy_isr.h
│   │   │   ├── vcos_logging_control.h
│   │   │   ├── vcos_logging.h
│   │   │   ├── vcos_lowlevel_thread.h
│   │   │   ├── vcos_mem.h
│   │   │   ├── vcos_mempool.h
│   │   │   ├── vcos_msgqueue.h
│   │   │   ├── vcos_mutex.h
│   │   │   ├── vcos_named_semaphore.h
│   │   │   ├── vcos_once.h
│   │   │   ├── vcos_queue.h
│   │   │   ├── vcos_quickslow_mutex.h
│   │   │   ├── vcos_reentrant_mutex.h
│   │   │   ├── vcos_semaphore.h
│   │   │   ├── vcos_stdbool.h
│   │   │   ├── vcos_stdint.h
│   │   │   ├── vcos_string.h
│   │   │   ├── vcos_thread_attr.h
│   │   │   ├── vcos_thread.h
│   │   │   ├── vcos_timer.h
│   │   │   ├── vcos_tls.h
│   │   │   └── vcos_types.h
│   │   ├── vcsm
│   │   │   └── user-vcsm.h
│   │   ├── vctypes
│   │   │   ├── vc_display_types.h
│   │   │   ├── vc_image_structs.h
│   │   │   └── vc_image_types.h
│   │   └── vmcs_host
│   │       ├── khronos
│   │       │   └── IL
│   │       │       ├── OMX_Audio.h
│   │       │       ├── OMX_Broadcom.h
│   │       │       ├── OMX_Component.h
│   │       │       ├── OMX_Core.h
│   │       │       ├── OMX_ILCS.h
│   │       │       ├── OMX_Image.h
│   │       │       ├── OMX_Index.h
│   │       │       ├── OMX_IVCommon.h
│   │       │       ├── OMX_Other.h
│   │       │       ├── OMX_Types.h
│   │       │       └── OMX_Video.h
│   │       ├── linux
│   │       │   └── vchost_config.h
│   │       ├── vc_cec.h
│   │       ├── vc_cecservice_defs.h
│   │       ├── vc_cecservice.h
│   │       ├── vc_cma.h
│   │       ├── vc_dispmanx.h
│   │       ├── vc_dispmanx_types.h
│   │       ├── vc_dispservice_defs.h
│   │       ├── vc_dispservice_x_defs.h
│   │       ├── vc_gencmd_defs.h
│   │       ├── vcgencmd.h
│   │       ├── vc_hdmi.h
│   │       ├── vc_hdmi_property.h
│   │       ├── vchost.h
│   │       ├── vchost_platform_config.h
│   │       ├── vcilcs_common.h
│   │       ├── vc_ilcs_defs.h
│   │       ├── vcilcs.h
│   │       ├── vc_imageconv_defs.h
│   │       ├── vc_sdtv.h
│   │       ├── vc_service_common.h
│   │       ├── vc_tvservice_defs.h
│   │       ├── vc_tvservice.h
│   │       ├── vc_vchi_audioserv_defs.h
│   │       ├── vc_vchi_bufman_defs.h
│   │       ├── vc_vchi_bufman.h
│   │       ├── vc_vchi_dispmanx.h
│   │       ├── vc_vchi_gencmd.h
│   │       └── vc_vchi_gpuserv.h
│   ├── KHR
│   │   └── khrplatform.h
│   ├── vcinclude
│   │   ├── common.h
│   │   ├── vc_image_types.h
│   │   └── vcore.h
│   ├── VG
│   │   ├── openvg.h
│   │   ├── vgext.h
│   │   ├── vgplatform.h
│   │   └── vgu.h
│   └── WF
│       ├── wfc.h
│       └── wfcplatform.h
├── lib
│   ├── libbcm_host.so
│   ├── libbrcmEGL.so
│   ├── libbrcmGLESv2.so
│   ├── libbrcmOpenVG.so
│   ├── libbrcmWFC.so
│   ├── libcontainers.so
│   ├── libdebug_sym.so
│   ├── libdebug_sym_static.a
│   ├── libdtovl.so
│   ├── libEGL_static.a
│   ├── libelftoolchain.so
│   ├── libGLESv2_static.a
│   ├── libkhrn_client.a
│   ├── libkhrn_static.a
│   ├── libmmal_components.so
│   ├── libmmal_core.so
│   ├── libmmal.so
│   ├── libmmal_util.so
│   ├── libmmal_vc_client.so
│   ├── libopenmaxil.so
│   ├── libvchiq_arm.so
│   ├── libvchostif.a
│   ├── libvcilcs.a
│   ├── libvcos.so
│   ├── libvcsm.so
│   ├── pkgconfig
│   │   ├── bcm_host.pc
│   │   ├── brcmegl.pc
│   │   ├── brcmglesv2.pc
│   │   ├── brcmvg.pc
│   │   ├── mmal.pc
│   │   └── vcsm.pc
│   └── plugins
│       └── plugins
│           ├── reader_asf.so
│           ├── reader_avi.so
│           ├── reader_binary.so
│           ├── reader_flv.so
│           ├── reader_metadata_id3.so
│           ├── reader_mkv.so
│           ├── reader_mp4.so
│           ├── reader_mpga.so
│           ├── reader_ps.so
│           ├── reader_qsynth.so
│           ├── reader_raw_video.so
│           ├── reader_rcv.so
│           ├── reader_rtp.so
│           ├── reader_rtsp.so
│           ├── reader_rv9.so
│           ├── reader_simple.so
│           ├── reader_wav.so
│           ├── writer_asf.so
│           ├── writer_avi.so
│           ├── writer_binary.so
│           ├── writer_dummy.so
│           ├── writer_mp4.so
│           ├── writer_raw_video.so
│           └── writer_simple.so
└── src
    └── hello_pi
        ├── hello_audio
        │   ├── audio.c
        │   ├── audioplay.h
        │   ├── Makefile
        │   └── sinewave.c
        ├── hello_dispmanx
        │   ├── dispmanx.c
        │   └── Makefile
        ├── hello_encode
        │   ├── encode.c
        │   └── Makefile
        ├── hello_fft
        │   ├── gpu_fft_base.c
        │   ├── gpu_fft.c
        │   ├── gpu_fft.h
        │   ├── gpu_fft_shaders.c
        │   ├── gpu_fft_trans.c
        │   ├── gpu_fft_trans.h
        │   ├── gpu_fft_twiddles.c
        │   ├── gpu_fft.txt
        │   ├── hello_fft_2d_bitmap.h
        │   ├── hello_fft_2d.c
        │   ├── hello_fft.c
        │   ├── hex
        │   │   ├── shader_1024k.hex
        │   │   ├── shader_128k.hex
        │   │   ├── shader_16k.hex
        │   │   ├── shader_1k.hex
        │   │   ├── shader_2048k.hex
        │   │   ├── shader_256.hex
        │   │   ├── shader_256k.hex
        │   │   ├── shader_2k.hex
        │   │   ├── shader_32k.hex
        │   │   ├── shader_4096k.hex
        │   │   ├── shader_4k.hex
        │   │   ├── shader_512.hex
        │   │   ├── shader_512k.hex
        │   │   ├── shader_64k.hex
        │   │   ├── shader_8k.hex
        │   │   └── shader_trans.hex
        │   ├── mailbox.c
        │   ├── mailbox.h
        │   ├── makefile
        │   └── qasm
        │       ├── gpu_fft_1024k.qasm
        │       ├── gpu_fft_128k.qasm
        │       ├── gpu_fft_16k.qasm
        │       ├── gpu_fft_1k.qasm
        │       ├── gpu_fft_2048k.qasm
        │       ├── gpu_fft_2048k.qinc
        │       ├── gpu_fft_256k.qasm
        │       ├── gpu_fft_256.qasm
        │       ├── gpu_fft_2k.qasm
        │       ├── gpu_fft_32k.qasm
        │       ├── gpu_fft_4096k.qasm
        │       ├── gpu_fft_4k.qasm
        │       ├── gpu_fft_512k.qasm
        │       ├── gpu_fft_512.qasm
        │       ├── gpu_fft_64k.qasm
        │       ├── gpu_fft_8k.qasm
        │       ├── gpu_fft_ex.qinc
        │       ├── gpu_fft.qinc
        │       └── gpu_fft_trans.qasm
        ├── hello_font
        │   ├── main.c
        │   ├── Makefile
        │   └── Vera.ttf
        ├── hello_jpeg
        │   ├── jpeg.c
        │   ├── jpeg.h
        │   └── Makefile
        ├── hello_mmal_encode
        │   ├── Makefile
        │   └── mmal_encode.c
        ├── hello_teapot
        │   ├── cube_texture_and_coords.h
        │   ├── Makefile
        │   ├── models.c
        │   ├── models.h
        │   ├── README.md
        │   ├── teapot.obj.dat
        │   ├── triangle.c
        │   ├── triangle.h
        │   └── video.c
        ├── hello_tiger
        │   ├── license.txt
        │   ├── main.c
        │   ├── Makefile
        │   ├── readme.txt
        │   ├── tiger.c
        │   └── tiger.h
        ├── hello_triangle
        │   ├── cube_texture_and_coords.h
        │   ├── Djenne_128_128.raw
        │   ├── Gaudi_128_128.raw
        │   ├── Lucca_128_128.raw
        │   ├── Makefile
        │   └── triangle.c
        ├── hello_triangle2
        │   ├── Makefile
        │   └── triangle2.c
        ├── hello_video
        │   ├── Makefile
        │   ├── README
        │   ├── test.h264
        │   └── video.c
        ├── hello_videocube
        │   ├── cube_texture_and_coords.h
        │   ├── Makefile
        │   ├── README.md
        │   ├── triangle.c
        │   ├── triangle.h
        │   └── video.c
        ├── hello_world
        │   ├── Makefile
        │   └── world.c
        ├── libs
        │   ├── ilclient
        │   │   ├── ilclient.c
        │   │   ├── ilclient.h
        │   │   ├── ilcore.c
        │   │   └── Makefile
        │   ├── revision
        │   │   ├── Makefile
        │   │   ├── revision.c
        │   │   └── revision.h
        │   └── vgfont
        │       ├── font.c
        │       ├── graphics.c
        │       ├── graphics_x_private.h
        │       ├── Makefile
        │       ├── vgfont.h
        │       ├── vgft.c
        │       └── vgft.h
        ├── Makefile
        ├── Makefile.include
        ├── README
        └── rebuild.sh

这些文件博主也不确定是否是系统刷好后就有的,因为中途折腾的时候安装过一些依赖,并且博主的系统自带了FFmpeg程序。常见需要安装的依赖有:libomxil-bellagio-dev等。

sudo apt-get install libomxil-bellagio-dev #在树莓派上安装

②.编写配置脚本

注意:最好不要在这个脚本中添加注释,或者把注释放到最后,续航符号\加上注释有时候会导致命令错误、配置出错。

#!/bin/sh

./configure \
--enable-shared \
--disable-static \
--prefix=`pwd`/pi_install \
--extra-cflags=-I/home/boy/opensource/vc/include/IL \
--extra-cflags=-I/home/boy/opensource/x264/pi_install/include \
--extra-ldflags=-L/home/boy/opensource/x264/pi_install/lib \
--extra-ldflags=-L/home/boy/opensource/vc/lib \
--enable-omx \
--enable-omx-rpi \
--enable-encoder=h264_omx \
--enable-libx264 \
--enable-cross-compile \
--cross-prefix=arm-linux-gnueabihf- \
--arch=arm \
--target-os=linux \
--enable-gpl

这几条即为配置omx硬件编码支持的选项:

--enable-omx \
--enable-omx-rpi \
--enable-encoder=h264_omx \

若出现提示缺少头文件等错误:

boy@ubuntu:~/opensource/ffmpeg$ ./pi_config.sh 
ERROR: OpenMAX IL headers from raspberrypi/firmware not found

通过extra-cflags、extra-ldflags添加外部头文件、库文件搜索路径可尝试解决,如下:

--extra-cflags=-I/home/boy/opensource/vc/include/IL \ #复制自树莓派/opt/vc目录,添加OpenMAX IL头文件路径
--extra-cflags=-I/home/boy/opensource/x264/pi_install/include \ #x264头文件路径
--extra-ldflags=-L/home/boy/opensource/x264/pi_install/lib \ #x264库文件路径
--extra-ldflags=-L/home/boy/opensource/vc/lib \ #复制自树莓派/opt/vc目录,omx、mmal等库路径

③.编译、安装

上一步没问题后,就会生成相应的makefile,执行make -j8多线程编译,完成后再make install即可在安装到相应路径:

boy@ubuntu:~/opensource/ffmpeg$ cd pi_install/
boy@ubuntu:~/opensource/ffmpeg/pi_install$ ls
bin  include  lib  share
boy@ubuntu:~/opensource/ffmpeg/pi_install$ 

相应的头文件、库文件添加到应用工程即可编译,在树莓派中调用的时候再添加这些相关的so库到库文件搜索路径即可。

使用硬件编码的程序,后续项目用上再放出来,未完待续~~

编码Demo测试

在doc/example目录下的encode_video.c有完整的FFmpeg的视频编码demo,将yuv420p编码为H264:

/*
 * Copyright (c) 2001 Fabrice Bellard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * @file
 * video encoding with libavcodec API example
 *
 * @example encode_video.c
 */

#include 
#include 
#include 

#include 

#include 
#include 

static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
                   FILE *outfile)
{
    int ret;

    /* send the frame to the encoder */
    if (frame)
        printf("Send frame %3"PRId64"\n", frame->pts);

    ret = avcodec_send_frame(enc_ctx, frame);
    if (ret < 0) {
        fprintf(stderr, "Error sending a frame for encoding\n");
        exit(1);
    }

    while (ret >= 0) {
        ret = avcodec_receive_packet(enc_ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }

        printf("Write packet %3"PRId64" (size=%5d)\n", pkt->pts, pkt->size);
        fwrite(pkt->data, 1, pkt->size, outfile);
        av_packet_unref(pkt);
    }
}

int main(int argc, char **argv)
{
    const char *filename, *codec_name;
    const AVCodec *codec;
    AVCodecContext *c= NULL;
    int i, ret, x, y;
    FILE *f;
    AVFrame *frame;
    AVPacket *pkt;
    uint8_t endcode[] = { 0, 0, 1, 0xb7 };

    if (argc <= 2) {
        fprintf(stderr, "Usage: %s  \n", argv[0]);
        exit(0);
    }
    filename = argv[1];
    codec_name = argv[2];

    /* find the mpeg1video encoder */
    codec = avcodec_find_encoder_by_name(codec_name);
    if (!codec) {
        fprintf(stderr, "Codec '%s' not found\n", codec_name);
        exit(1);
    }

    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    pkt = av_packet_alloc();
    if (!pkt)
        exit(1);

    /* put sample parameters */
    c->bit_rate = 400000;
    /* resolution must be a multiple of two */
    c->width = 352;
    c->height = 288;
    /* frames per second */
    c->time_base = (AVRational){1, 25};
    c->framerate = (AVRational){25, 1};

    /* emit one intra frame every ten frames
     * check frame pict_type before passing frame
     * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
     * then gop_size is ignored and the output of encoder
     * will always be I frame irrespective to gop_size
     */
    c->gop_size = 10;
    c->max_b_frames = 1;
    c->pix_fmt = AV_PIX_FMT_YUV420P;

    if (codec->id == AV_CODEC_ID_H264)
        av_opt_set(c->priv_data, "preset", "slow", 0);

    /* open it */
    ret = avcodec_open2(c, codec, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
        exit(1);
    }

    f = fopen(filename, "wb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
    frame->format = c->pix_fmt;
    frame->width  = c->width;
    frame->height = c->height;

    ret = av_frame_get_buffer(frame, 0);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate the video frame data\n");
        exit(1);
    }

    /* encode 1 second of video */
    for (i = 0; i < 25; i++) {
        fflush(stdout);

        /* Make sure the frame data is writable.
           On the first round, the frame is fresh from av_frame_get_buffer()
           and therefore we know it is writable.
           But on the next rounds, encode() will have called
           avcodec_send_frame(), and the codec may have kept a reference to
           the frame in its internal structures, that makes the frame
           unwritable.
           av_frame_make_writable() checks that and allocates a new buffer
           for the frame only if necessary.
         */
        ret = av_frame_make_writable(frame);
        if (ret < 0)
            exit(1);

        /* Prepare a dummy image.
           In real code, this is where you would have your own logic for
           filling the frame. FFmpeg does not care what you put in the
           frame.
         */
        /* Y */
        for (y = 0; y < c->height; y++) {
            for (x = 0; x < c->width; x++) {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
            }
        }

        /* Cb and Cr */
        for (y = 0; y < c->height/2; y++) {
            for (x = 0; x < c->width/2; x++) {
                frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
            }
        }

        frame->pts = i;

        /* encode the image */
        encode(c, frame, pkt, f);
    }

    /* flush the encoder */
    encode(c, NULL, pkt, f);

    /* Add sequence end code to have a real MPEG file.
       It makes only sense because this tiny examples writes packets
       directly. This is called "elementary stream" and only works for some
       codecs. To create a valid file, you usually need to write packets
       into a proper file format or protocol; see muxing.c.
     */
    if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO)
        fwrite(endcode, 1, sizeof(endcode), f);
    fclose(f);

    avcodec_free_context(&c);
    av_frame_free(&frame);
    av_packet_free(&pkt);

    return 0;
}

简单构建一个工程(基于CMake构建),工程目录如下:

boy@ubuntu:~/encode_test$ ls -l
总用量 20
-rwxrwxr-x  1 boy boy  285 45 23:04 build.sh
-rw-rw-r--  1 boy boy  864 41 23:39 CMakeLists.txt
drwxrwxr-x 10 boy boy 4096 41 23:30 include
drwxrwxr-x  2 boy boy 4096 41 23:31 lib
drwxrwxr-x  2 boy boy 4096 41 23:32 src

lib下即交叉编译生成的相应so库文件,include为对应的头文件,src即为encode_video.c源码,CMake如下:

cmake_minimum_required(VERSION 3.0.0)
# 设置编译工具链
set(GCC "/opt/rpi_tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc")
set(GXX "/opt/rpi_tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-g++")
set(CMAKE_C_COMPLIER ${GCC})
set(CMAKE_CXX_COMPLIER ${GXX})
# 工程名
project(enc_test VERSION 0.1.0)

# 设置头文件查找路径
include_directories(
    ${PROJECT_SOURCE_DIR}/include
)
# 查找src目录下的源文件,赋值到SRCS变量
aux_source_directory(${PROJECT_SOURCE_DIR}/src SRCS)
# 设置库文件搜索路径
link_directories(
    "${PROJECT_SOURCE_DIR}/lib"
)
# 生成可执行文件
add_executable(${PROJECT_NAME} ${SRCS})

#指定链接库
target_link_libraries(
    ${PROJECT_NAME}
    avcodec
    avdevice
    avfilter
    avformat
    avutil
    postproc
    swresample
    swscale
)

cmake生成makefile后,将make得到的可执行文件即可放到树莓派中运行测试。

添加相关so库

将x264和FFmpeg编译生成的库文件放到树莓派的库搜索路径中,然后运行测试:

pi@raspberrypi:~ $ ./enc_test out.h264 libx264

此时就会调用x264编码器,将ffmpeg生成的yuv420p格式的帧转换为h264帧输出到文件out.h264中:

[libx264 @ 0x1e20020] using cpu capabilities: none!
[libx264 @ 0x1e20020] profile High, level 1.3, 4:2:0, 8-bit
Send frame   0
Send frame   1
Send frame   2
Send frame   3
Send frame   4
Send frame   5
Send frame   6
Send frame   7
Send frame   8
Send frame   9
Send frame  10
Send frame  11
Send frame  12
Send frame  13
Send frame  14
Send frame  15
Send frame  16
Send frame  17
Send frame  18
Write packet   0 (size= 2068)
Send frame  19
Write packet   2 (size=  674)
Send frame  20
Write packet   1 (size=  133)
Send frame  21
Write packet   4 (size=  767)
Send frame  22
Write packet   3 (size=  193)
Send frame  23
Write packet   6 (size=  682)
Send frame  24
Write packet   5 (size=  521)
Write packet   8 (size=  868)
Write packet   7 (size=  484)
Write packet   9 (size=  638)
Write packet  10 (size= 2304)
Write packet  12 (size= 1311)
Write packet  11 (size=  829)
Write packet  14 (size=  866)
Write packet  13 (size=  523)
Write packet  16 (size=  831)
Write packet  15 (size=  635)
Write packet  18 (size= 1034)
Write packet  17 (size=  576)
Write packet  19 (size=  682)
Write packet  20 (size= 2477)
Write packet  22 (size= 1415)
Write packet  21 (size=  997)
Write packet  24 (size=  862)
Write packet  23 (size=  878)
[libx264 @ 0x1e20020] frame I:3     Avg QP:24.66  size:  2283
[libx264 @ 0x1e20020] frame P:12    Avg QP:23.14  size:   886
[libx264 @ 0x1e20020] frame B:10    Avg QP:27.26  size:   577
[libx264 @ 0x1e20020] consecutive B-frames: 20.0% 80.0%
[libx264 @ 0x1e20020] mb I  I16..4: 77.3% 12.6% 10.1%
[libx264 @ 0x1e20020] mb P  I16..4: 76.7%  1.1%  0.2%  P16..4: 20.4%  1.2%  0.4%  0.0%  0.0%    skip: 0.0%
[libx264 @ 0x1e20020] mb B  I16..4:  0.0%  0.0%  0.0%  B16..8: 13.9%  0.2%  0.0%  direct:12.4%  skip:73.5%  L0:25.0% L1:57.0% BI:18.1%
[libx264 @ 0x1e20020] final ratefactor: 15.47
[libx264 @ 0x1e20020] 8x8 transform intra:4.2% inter:16.2%
[libx264 @ 0x1e20020] direct mvs  spatial:0.0% temporal:100.0%
[libx264 @ 0x1e20020] coded y,uvDC,uvAC intra: 5.4% 30.6% 4.6% inter: 1.3% 39.8% 9.0%
[libx264 @ 0x1e20020] i16 v,h,dc,p:  0%  0%  0% 100%
[libx264 @ 0x1e20020] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu:  1% 38% 12% 48%  0%  0%  0%  0%  1%
[libx264 @ 0x1e20020] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu:  8%  5% 13% 54%  1%  6%  5%  5%  2%
[libx264 @ 0x1e20020] i8c dc,h,v,p:  1%  7%  5% 87%
[libx264 @ 0x1e20020] Weighted P-Frames: Y:0.0% UV:0.0%
[libx264 @ 0x1e20020] ref P L0: 97.5%  0.6%  1.5%  0.2%  0.1%  0.1%
[libx264 @ 0x1e20020] ref B L0: 60.6% 33.1%  6.2%
[libx264 @ 0x1e20020] kb/s:185.98	

效果

生成H264文件使用VLC播放器播放如下:

树莓派移植FFmepg记录(x264、硬件编码支持)_第1张图片

你可能感兴趣的:(#,树莓派视频监控,ffmpeg,linux,硬件工程)