Ubuntu 16.04.5下FFmpeg编译与开发环境搭建
PC环境: Ubuntu 18.04 上面只要安装下面的提示安装即可,基本上不必再下载依赖库的源代码进行编译和安装
编译步骤:
1, 安装相关工具:
- sudo apt install -y autoconf automake build-essential git libass-dev libfreetype6-dev libsdl2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo wget zlib1g-dev
- sudo apt install -y nasm yasm cmake mercurial
2,git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg,得到文件夹ffmpeg,进入ffmpeg文件夹:
- ./configure --enable-shared --prefix=/usr/local/ffmpeg
安装到/usr/local/ffmpeg下,可通过“--prefix=安装目录”进行修改。--enable-shared:指定生成动态库,默认是静态库。静态库不方便后续开发。
2.sudo make
3.sudo make install
3,添加ffmpeg库的链接:
在/etc/ld.so.conf中 末尾添加 /usr/local/ffmpeg/lib 即可,执行
- sudo ldconfig
4.安装VS Code
参考https://blog.csdn.net/u011258217/article/details/78693564
--------------------------------------------------- ubuntu 16.04.5 下还需要按照一下提示进行安装依赖 -------------------------------------
sudo apt-get update sudo apt-get -y --force-yes install autoconf automake build-essential libass-dev libfreetype6-dev \ libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev \ libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev
mkdir ~/ffmpeg_sources
sudo apt install yasm libx264 libx265 libfdk-aac libmp3lame libopus libvpx -y
sudo apt install yasm libx264-dev libx265-dev libfdk-aac-dev libmp3lame-dev libopus-dev libvpx-dev -y
cd ~/ffmpeg_sources
wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
tar xzvf yasm-1.3.0.tar.gz
cd yasm-1.3.0
./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin"
make
make install
make distclean
cd ~/ffmpeg_sources
wget http://download.videolan.org/pub/x264/snapshots/last_x264.tar.bz2
tar xjvf last_x264.tar.bz2
cd x264-snapshot*
PATH="$HOME/bin:$PATH" ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --enable-static
PATH="$HOME/bin:$PATH" make
make install
make distclean
问题:不知道是不是环境变量的关系,很多标准command不能用,参考另一个笔记:linux下提示bash:command not found
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
5、libx265
265的源一直没下载下来,也暂时不用x265,所以没有安装,后续编译ffmpeg不编译x265就可以。
6、libfdk-aac
cd ~/ffmpeg_sources
wget -O fdk-aac.tar.gz https://github.com/mstorsjo/fdk-aac/tarball/master
git clone https://github.com/mstorsjo/fdk-aac.git
https://github.com/mstorsjo/fdk-aac/archive/master.zip
tar xzvf fdk-aac.tar.gz
cd mstorsjo-fdk-aac*
autoreconf -fiv
./configure --prefix="$HOME/ffmpeg_build" --disable-shared
make
make install
make distclean
sudo apt-get install nasm
cd ~/ffmpeg_sources
wget http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz
tar xzvf lame-3.99.5.tar.gz
cd lame-3.99.5
./configure --prefix="$HOME/ffmpeg_build" --enable-nasm --disable-shared
make
make install
make distclean
cd ~/ffmpeg_sources
wget http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz
tar xzvf opus-1.1.tar.gz
cd opus-1.1
./configure --prefix="$HOME/ffmpeg_build" --disable-shared
make
make install
make distclean
cd ~/ffmpeg_sources
wget http://webm.googlecode.com/files/libvpx-v1.3.0.tar.bz2 ## !!!!! 这个应该下不下来,可以百度一下替代, 换上最新版本的,不要用这个,有点太低了。
http://ftp.osuosl.org/pub/blfs/conglomeration/libvpx/
wget -c http://ftp.osuosl.org/pub/blfs/conglomeration/libvpx/libvpx-1.7.0.tar.gz
tar xjvf libvpx-v1.3.0.tar.bz2 cd libvpx-v1.3.0 PATH="$HOME/bin:$PATH" ./configure --prefix="$HOME/ffmpeg_build" --disable-examples --disable-unit-tests PATH="$HOME/bin:$PATH" make make install make clean
## 下面的这个不必了,直接上github上下载源码即可, https://github.com/FFmpeg/FFmpeg.git
安装编译ffmpeg
cd ~/ffmpeg_sources wget http://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2 tar xjvf ffmpeg-snapshot.tar.bz2 cd ffmpeg PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure \ --prefix="$HOME/ffmpeg_build" \ --pkg-config-flags="--static" \ --extra-cflags="-I$HOME/ffmpeg_build/include" \ --extra-ldflags="-L$HOME/ffmpeg_build/lib" \ --bindir="$HOME/bin" \ --enable-gpl \ --enable-libass \ --enable-libfdk-aac \ --enable-libfreetype \ --enable-libmp3lame \ --enable-libopus \ --enable-libtheora \ --enable-libvorbis \ --enable-libvpx \ --enable-libx264 \ --enable-libx265 \ --enable-nonfree PATH="$HOME/bin:$PATH" make make install make distclean hash -r
查看ffmpeg是否安装成功,用ffmpeg -version
之后我们就可以使用ffmpeg 将文件转化成ts文件,以备m3u8-segmenter切片,搭建hls流媒体服务器。
另外有参考源代码:
https://github.com/lspbeyond/p264decoder.git
思科家的: https://github.com/cisco/openh264.git
--------------------------------------------------------------------------- 分割线 -------------------------------------------------------------------------
5.测试环境
#include <stdio.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <libavfilter/avfilter.h> #define dbmsgc(fmt, args ...) printf("cong:%s[%d]: "fmt"\n", __FUNCTION__, __LINE__,##args) //#define dbmsg(fmt, args ...) printf("cong:%s:%s[%d]: "fmt"\n",__FILE__, __FUNCTION__, __LINE__,##args) int main(int argc, char **argv) { int i=0; AVFormatContext *pFormatCtx = NULL; avcodec_register_all(); #if CONFIG_AVDEVICE avdevice_register_all(); #endif avfilter_register_all(); av_register_all(); if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0) return -1; // Couldn't open file if(avformat_find_stream_info(pFormatCtx, NULL)<0) return -1; // Couldn't find stream inform av_dump_format(pFormatCtx,0, 0, 0); return 0; }
6..编写Makefile
FFMPEG=/usr/local/ffmpeg CC=gcc CFLAGS=-g -I$(FFMPEG)/include LDFLAGS = -L$(FFMPEG)/lib/ -lswscale -lswresample -lavformat -lavdevice -lavcodec -lavutil -lavfilter -lm TARGETS=test all: $(TARGETS) test:test.c $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -std=c++11 #注意这里的-std=c++11 clean: rm -rf $(TARGETS)
7.make
8../test
测试代码2:
/* * Copyright (c) 2015 Ludmila Glinskih * * 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. */ /** * H264 codec test. */ #include "libavutil/adler32.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/imgutils.h" static int video_decode_example(const char *input_filename) { AVCodec *codec = NULL; AVCodecContext *ctx= NULL; AVCodecParameters *origin_par = NULL; AVFrame *fr = NULL; uint8_t *byte_buffer = NULL; AVPacket pkt; AVFormatContext *fmt_ctx = NULL; int number_of_written_bytes; int video_stream; int got_frame = 0; int byte_buffer_size; int i = 0; int result; int end_of_stream = 0; result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL); if (result < 0) { av_log(NULL, AV_LOG_ERROR, "Can't open file\n"); return result; } result = avformat_find_stream_info(fmt_ctx, NULL); if (result < 0) { av_log(NULL, AV_LOG_ERROR, "Can't get stream info\n"); return result; } video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if (video_stream < 0) { av_log(NULL, AV_LOG_ERROR, "Can't find video stream in input file\n"); return -1; } origin_par = fmt_ctx->streams[video_stream]->codecpar; codec = avcodec_find_decoder(origin_par->codec_id); if (!codec) { av_log(NULL, AV_LOG_ERROR, "Can't find decoder\n"); return -1; } ctx = avcodec_alloc_context3(codec); if (!ctx) { av_log(NULL, AV_LOG_ERROR, "Can't allocate decoder context\n"); return AVERROR(ENOMEM); } result = avcodec_parameters_to_context(ctx, origin_par); if (result) { av_log(NULL, AV_LOG_ERROR, "Can't copy decoder context\n"); return result; } result = avcodec_open2(ctx, codec, NULL); if (result < 0) { av_log(ctx, AV_LOG_ERROR, "Can't open decoder\n"); return result; } fr = av_frame_alloc(); if (!fr) { av_log(NULL, AV_LOG_ERROR, "Can't allocate frame\n"); return AVERROR(ENOMEM); } byte_buffer_size = av_image_get_buffer_size(ctx->pix_fmt, ctx->width, ctx->height, 16); byte_buffer = av_malloc(byte_buffer_size); if (!byte_buffer) { av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n"); return AVERROR(ENOMEM); } printf("#tb %d: %d/%d\n", video_stream, fmt_ctx->streams[video_stream]->time_base.num, fmt_ctx->streams[video_stream]->time_base.den); i = 0; av_init_packet(&pkt); do { if (!end_of_stream) if (av_read_frame(fmt_ctx, &pkt) < 0) end_of_stream = 1; if (end_of_stream) { pkt.data = NULL; pkt.size = 0; } if (pkt.stream_index == video_stream || end_of_stream) { got_frame = 0; if (pkt.pts == AV_NOPTS_VALUE) pkt.pts = pkt.dts = i; result = avcodec_decode_video2(ctx, fr, &got_frame, &pkt); if (result < 0) { av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n"); return result; } if (got_frame) { number_of_written_bytes = av_image_copy_to_buffer(byte_buffer, byte_buffer_size, (const uint8_t* const *)fr->data, (const int*) fr->linesize, ctx->pix_fmt, ctx->width, ctx->height, 1); if (number_of_written_bytes < 0) { av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n"); return number_of_written_bytes; } printf("%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, 0x%08lx\n", video_stream, fr->pts, fr->pkt_dts, fr->pkt_duration, number_of_written_bytes, av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes)); } av_packet_unref(&pkt); av_init_packet(&pkt); } i++; } while (!end_of_stream || got_frame); av_packet_unref(&pkt); av_frame_free(&fr); avcodec_close(ctx); avformat_close_input(&fmt_ctx); avcodec_free_context(&ctx); av_freep(&byte_buffer); return 0; } int main(int argc, char **argv) { if (argc < 2) { av_log(NULL, AV_LOG_ERROR, "Incorrect input\n"); return 1; } if (video_decode_example(argv[1]) != 0) return 1; return 0; }
测试代码3:
/* * Copyright (c) 2015 Ludmila Glinskih * * 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. */ /** * draw_horiz_band test. */ #include "libavutil/adler32.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/imgutils.h" uint8_t *slice_byte_buffer; uint8_t slice_byte_buffer_size; int draw_horiz_band_called; static void draw_horiz_band(AVCodecContext *ctx, const AVFrame *fr, int offset[4], int slice_position, int type, int height) { int i; const AVPixFmtDescriptor *pix_fmt_desc; int chroma_w, chroma_h; int shift_slice_position; int shift_height; draw_horiz_band_called = 1; pix_fmt_desc = av_pix_fmt_desc_get(ctx->pix_fmt); chroma_w = -((-ctx->width) >> pix_fmt_desc->log2_chroma_w); chroma_h = -((-height) >> pix_fmt_desc->log2_chroma_h); shift_slice_position = -((-slice_position) >> pix_fmt_desc->log2_chroma_h); shift_height = -((-ctx->height) >> pix_fmt_desc->log2_chroma_h); for (i = 0; i < height; i++) { memcpy(slice_byte_buffer + ctx->width * slice_position + i * ctx->width, fr->data[0] + offset[0] + i * fr->linesize[0], ctx->width); } for (i = 0; i < chroma_h; i++) { memcpy(slice_byte_buffer + ctx->width * ctx->height + chroma_w * shift_slice_position + i * chroma_w, fr->data[1] + offset[1] + i * fr->linesize[1], chroma_w); } for (i = 0; i < chroma_h; i++) { memcpy(slice_byte_buffer + ctx->width * ctx->height + chroma_w * shift_height + chroma_w * shift_slice_position + i * chroma_w, fr->data[2] + offset[2] + i * fr->linesize[2], chroma_w); } } static int video_decode(const char *input_filename) { AVCodec *codec = NULL; AVCodecContext *ctx= NULL; AVCodecParameters *origin_par = NULL; uint8_t *byte_buffer = NULL; AVFrame *fr = NULL; AVPacket pkt; AVFormatContext *fmt_ctx = NULL; int number_of_written_bytes; int video_stream; int got_frame = 0; int byte_buffer_size; int result; int end_of_stream = 0; draw_horiz_band_called = 0; result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL); if (result < 0) { av_log(NULL, AV_LOG_ERROR, "Can't open file\n"); return result; } result = avformat_find_stream_info(fmt_ctx, NULL); if (result < 0) { av_log(NULL, AV_LOG_ERROR, "Can't get stream info\n"); return result; } video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if (video_stream < 0) { av_log(NULL, AV_LOG_ERROR, "Can't find video stream in input file\n"); return -1; } origin_par = fmt_ctx->streams[video_stream]->codecpar; codec = avcodec_find_decoder(origin_par->codec_id); if (!codec) { av_log(NULL, AV_LOG_ERROR, "Can't find decoder\n"); return -1; } ctx = avcodec_alloc_context3(codec); if (!ctx) { av_log(NULL, AV_LOG_ERROR, "Can't allocate decoder context\n"); return AVERROR(ENOMEM); } result = avcodec_parameters_to_context(ctx, origin_par); if (result) { av_log(NULL, AV_LOG_ERROR, "Can't copy decoder context\n"); return result; } ctx->draw_horiz_band = draw_horiz_band; ctx->thread_count = 1; result = avcodec_open2(ctx, codec, NULL); if (result < 0) { av_log(ctx, AV_LOG_ERROR, "Can't open decoder\n"); return result; } fr = av_frame_alloc(); if (!fr) { av_log(NULL, AV_LOG_ERROR, "Can't allocate frame\n"); return AVERROR(ENOMEM); } if (strcmp(codec->name, "flv") && strcmp(codec->name, "mpeg4") && strcmp(codec->name, "huffyuv")) { av_log(NULL, AV_LOG_ERROR, "Wrong codec\n"); return -1; } byte_buffer_size = av_image_get_buffer_size(ctx->pix_fmt, ctx->width, ctx->height, 32); byte_buffer = av_malloc(byte_buffer_size); if (!byte_buffer) { av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n"); return AVERROR(ENOMEM); } slice_byte_buffer = av_malloc(byte_buffer_size); if (!slice_byte_buffer) { av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n"); return AVERROR(ENOMEM); } memset(slice_byte_buffer, 0, byte_buffer_size); slice_byte_buffer_size = byte_buffer_size; av_init_packet(&pkt); do { if (!end_of_stream) { if (av_read_frame(fmt_ctx, &pkt) < 0) { end_of_stream = 1; } } if (end_of_stream) { pkt.data = NULL; pkt.size = 0; } if (pkt.stream_index == video_stream || end_of_stream) { got_frame = 0; result = avcodec_decode_video2(ctx, fr, &got_frame, &pkt); if (result < 0) { av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n"); return result; } if (got_frame) { number_of_written_bytes = av_image_copy_to_buffer(byte_buffer, byte_buffer_size, (const uint8_t* const *)fr->data, (const int*) fr->linesize, ctx->pix_fmt, ctx->width, ctx->height, 1); if (number_of_written_bytes < 0) { av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n"); return number_of_written_bytes; } if (draw_horiz_band_called == 0) { av_log(NULL, AV_LOG_ERROR, "draw_horiz_band haven't been called!\n"); return -1; } if (av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes) != av_adler32_update(0, (const uint8_t*)slice_byte_buffer, number_of_written_bytes)) { av_log(NULL, AV_LOG_ERROR, "Decoded frames with and without draw_horiz_band are not the same!\n"); return -1; } } av_packet_unref(&pkt); av_init_packet(&pkt); } } while (!end_of_stream || got_frame); av_packet_unref(&pkt); av_frame_free(&fr); avcodec_close(ctx); avformat_close_input(&fmt_ctx); avcodec_free_context(&ctx); av_freep(&byte_buffer); av_freep(&slice_byte_buffer); return 0; } int main(int argc, char **argv) { if (argc < 2) { av_log(NULL, AV_LOG_ERROR, "Incorrect input: expected %s <name of a video file>\nNote that test works only for huffyuv, flv and mpeg4 decoders\n", argv[0]); return 1; } if (video_decode(argv[1]) != 0) return 1; return 0; }
本文参考了:
https://blog.csdn.net/wawayu_0/article/details/80564349
https://www.cnblogs.com/candycaicai/p/4689459.html
特此致谢。
每一个不曾起舞的日子,都是对生命的辜负。
But it is the same with man as with the tree. The more he seeks to rise into the height and light, the more vigorously do his roots struggle earthward, downward, into the dark, the deep - into evil.
其实人跟树是一样的,越是向往高处的阳光,它的根就越要伸向黑暗的地底。----尼采