基于FFmpeg和SDL1.2的极简播放器实现

文章详细介绍了如何使用FFmpeg库和SDL库编译一个简单的视频播放器。主要步骤包括编译FFmpeg和SDL库,然后编写主程序,通过音视频调度实现播放功能。代码示例中包含了音频和视频处理的函数,以及同步和播放控制的逻辑。
摘要由CSDN通过智能技术生成

思路

基于FFmpeg写一个播放器,其实十分的简单。实际上,主要是对FFmpeg的API的封装,同时,我们需要将音视频通过主机呈现出来,所以还依赖于平台的SDL库,整体步骤和思路如下:

1. 编译用于音视频解码的FFmpeg库;

2. 编译用于音视频呈现的SDL库;

3. 编写主程序完成对音视频的整个调度过程;

编译FFmpeg库

这个步骤在《与FFmpeg的初次邂逅》(404

编译SDL库

这里SDL库我们依赖于1.2.15来进行编译,而不是最新的2.0版本;所以首先到SDL的官方网站http://www.libsdl.org/下载1.2.15版本的源代码进行傻瓜式的编译即可,这里需要注意我们配置的SDL生成库和头文件的位置。

ffmpeg@ubuntu:~/work/test$ tar xzvf SDL-1.2.15.tar.gz 
ffmpeg@ubuntu:~/work/test$ cd SDL-1.2.15/
ffmpeg@ubuntu:~/work/test/SDL-1.2.15$ ./configure  --prefix=/home/ffmpeg/work/SDL-1.2.15/out
ffmpeg@ubuntu:~/work/test/SDL-1.2.15$ make

编译过程中可能会遇到如下错误,

./src/video/x11/SDL_x11sym.h:168:17: error: conflicting types for ‘_XData32’
 SDL_X11_SYM(int,_XData32,(Display *dpy,register long *data,unsigned len),(dpy,data,len),return)
                 ^
./src/video/x11/SDL_x11dyn.c:95:5: note: in definition of macro ‘SDL_X11_SYM’
  rc fn params { ret p##fn args ; }
     ^
In file included from ./src/video/x11/SDL_x11dyn.h:34:0,
                 from ./src/video/x11/SDL_x11dyn.c:26:
/usr/include/X11/Xlibint.h:568:12: note: previous declaration of ‘_XData32’ was here
 extern int _XData32(
            ^
build-deps:1129: recipe for target 'build/SDL_x11dyn.lo' failed
make: *** [build/SDL_x11dyn.lo] Error 1
ffmpeg@ubuntu:~/work/SDL-1.2.15$ 

请参考如下方法修改(http://blog.csdn.net/jhting/article/details/38523945),

-SDL_X11_SYM(int,_XData32,(Display *dpy,register long *data,unsigned len),(dpy,data,len),return)  
+SDL_X11_SYM(int,_XData32,(Display *dpy,register _Xconst long *data,unsigned len),(dpy,data,len),return)  

xplayer播放器

暂且叫我们这个播放器叫xplayer吧,对于代码我们采用Makefile的方式进行管理,其目录结构如下:

下面是整个播放器的Makefile文件:

# xplayer Makefile Sample

# List Compiler Tools
CC = gcc 
XX = g++ 
CFLAGS = -Wall -O -g

# Compile Target
TARGET = xplayer    

# Include files
INCDIR = /home/ffmpeg/work/ffmpeg-3.2.4/out/include
INCDIR += /home/ffmpeg/work/ffmpeg-3.2.4
INCDIR += /home/ffmpeg/work/SDL-1.2.15/out/include

INCLUDE = $(foreach dir, $(INCDIR), -I$(dir))

LIBPATH = /home/ffmpeg/work/ffmpeg-3.2.4/out/lib
LIBPATH += /home/ffmpeg/work/SDL-1.2.15/out/lib 

LIBSPATH = $(foreach dir, $(LIBPATH), -L$(dir))

# needs to be in linking order
LIB = avfilter avformat avcodec swresample swscale avutil pthread z SDL dl asound

LIBS := $(foreach n,$(LIB),-l$(n))

# Depend on
%.o: %.c 
    $(CC) $(INCLUDE) -c $< -o $@ $(CFLAGS)

%.o:%.cpp 
    $(XX) $(INCLUDE) -c $< -o $@ $(CFLAGS)

# Source Code
SOURCES = $(wildcard *.c *.cpp) 

# Objs File
OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))

# BIN depend on
$(TARGET) : $(OBJS) 
    $(XX) -o $(TARGET) $(OBJS) $(LIBS) $(LIBSPATH) 
    chmod a+x $(TARGET) 

# clean
clean : 
    rm -rf $(OBJS)
    rm -rf $(TARGET)

下面是音频相关的audio.c的文件代码:

/*
 * Copyright (c) 2017 ericbaba
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */


#include "xplayer.h"

static AVFilterContext *in_audio_filter;   // the first filter in the audio chain
static AVFilterContext *out_audio_filter;  // the last filter in the audio chain
static AVFilterGraph *agraph;              // audio filter graph
static struct AudioParams audio_filter_src;
static double audio_diff_cum; /* used for AV difference average computation */
static double audio_diff_avg_coef;
static double audio_diff_threshold;
static int audio_diff_avg_count;
static double audio_clock;
static int audio_buf_size;
static int audio_buf_index;


static int synchronize_audio(short *samples, int samples_size)
{
    int n;
    double ref_clock;
    double diff, avg_diff;
    int wanted_size, min_size, max_size;

    ref_clock = get_master_clock();
    diff = get_audio_clock() - ref_clock;

    if(diff < AV_NOSYNC_THRESHOLD) 
    {
        // accumulate the diffs
        audio_diff_cum = diff + audio_diff_avg_coef * audio_diff_cum;
        if(audio_diff_avg_count < AUDIO_DIFF_AVG_NB)
        {
            audio_diff_avg_count++;
        }
        else
        {
            avg_diff = audio_diff_cum * (1.0 - audio_diff_avg_coef);

            if(fabs(avg_diff) >= audio_diff_threshold) 
            {
                n = (2 * global_context.acodec_ctx->channels);
                wanted_size = samples_size + ((int)(diff * global_context.acodec_ctx->sample_rate) * n);
                min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX)  / 100);
                max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX)  / 100);
                if(wanted_size < min_size) 
                {
                    wanted_size = min_size;
                } 
                else if (wanted_size > max_size) 
                {
                    wanted_size = max_size;
                }

                if(wanted_size < samples_size) 
                {
                    samples_size = wanted_size;
                } 
                else if(wanted_size > samples_size)
                {
                    uint8_t *samples_end, *q;
                    int nb;

                    nb = (samples_size - wanted_size);
                    samples_end = (uint8_t *)samples + samples_size - n;
                    q = samples_end + n;
                    while(nb > 0) 
                    {
                        memcpy(q, samples_end, n);
                        q += n;
                        nb -= n;
                    }
                    samples_size = wanted_size;
                }
            }
        }
    }
    else 
    {
        audio_diff_avg_count = 0;
        audio_diff_cum = 0;
    }

    return samples_size;

}


static int init_filter_graph(AVFilterGraph **graph, AVFilterContext **src, AVFilterContext **sink)
{
    AVFilterGraph *filter_graph;
    AVFilterContext *abuffer_ctx;
    AVFilter        *abuffer;
    AVFilterContext *aformat_ctx;
    AVFilter        *aformat;
    AVFilterContext *abuffersink_ctx;
    AVFilter        *abuffersink;

    uint8_t options_str[1024];
    uint8_t ch_layout[64];

    int err;

    /* Create a new filtergraph, which will contain all the filters. */
    filter_graph = avfilter_graph_alloc();
    if (!filter_graph) 
    {
        av_log(NULL, AV_LOG_ERROR, "Unable to create filter graph.\n");
        return AVERROR(ENOMEM);
    }

    /* Create the abuffer filter;
    * it will be used for feeding the data into the graph. */
    abuffer = avfilter_get_by_name("abuffer");
    if (!abuffer)
    {
        av_log(NULL, AV_LOG_ERROR, "Could not find the abuffer filter.\n");
        return AVERROR_FILTER_NOT_FOUND;
    }

    abuffer_ctx = avfilter_graph_alloc_filter(filter_graph, abuffer, "src");
    if (!abuffer_ctx)
    {
        av_log(NULL, AV_LOG_ERROR, "Could not allocate the abuffer instance.\n");
        return AVERROR(ENOMEM);
    }

    /* Set the filter options through the AVOptions API. */
    av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, audio_filter_src.channel_layout);
    av_opt
如何用FFmpeg编写一个简单播放器详细步骤介绍 FFMPEG一个很好的库,可以用来创建视频应用或者生成特定的工具。FFMPEG 几乎为你把所有的繁重工作都做了,比 如解码、编码、复用和解复用。这使得 多媒体应用程序变得容易编写。它是一个简单的,用 C 编写的,快速的并且能够 解码 几乎所有你能用到的格式,当然也包括编码多种格式。 唯一的问题是它的文档基本上是没有的。有一个单独的指导讲了它的基本原理另 外还有一个使用 doxygen 生成的文档。这就是为什么当我决定研究 FFMPEG 来弄 清楚音视频应用程序是如何工作的过程中,我决定把这个过程用文档的形式记录 并且发布出来作为初学指导的原因。 在 FFMPEG 工程中有一个示例的程序叫作 ffplay。它是一个用 C 编写的利用 ffmpeg实现完整视频播放的简单播放器。这个指导将从原来 Martin Bohme 写 的一个更新版本的指导开始(我借鉴了一些),基于 Fabrice Bellard 的 ffplay, 我将从那里开发一个可以使用的视频播放器。在每一个指导中,我将介 绍一个 或者两个新的思想并且讲解我们如何来实现它。每一个指导都会有一个 C 源文 件,你可以下载,编译并沿着这条思路来自己做。源文件将向你展示一个真正 的 程序是如何运行,我们如何来调用所有的部件,也将告诉你在这个指导中技术实 现的细节并不重要。当我们结束这个指导的时候,我 们将有一个少于 1000 行代 码的可以工作的视频播放器。 在写播放器的过程中,我们将使用 SDL 来输出音频和视频。SDL一个优秀的跨 平台的多媒体库,被用在 MPEG 播放、模拟器和很多视频游戏中。你将需要下载 并安装 SDL 开发库到你的系统中,以便于编译这个指导中的程序。 这篇指导适用于具有相当编程背景的人。至少至少应该懂得 C 并且有队列和互斥 量等概念。你应当了解基本的多媒体中的像波形一类的概念,但是你不必知道的 太 多,因为我将在这篇指导中介绍很多这样的概念。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值