美文网首页FFmpeg专辑
FFmpeg编程开发笔记 —— 基于 ffplay 的Andro

FFmpeg编程开发笔记 —— 基于 ffplay 的Andro

作者: cain_huang | 来源:发表于2018-03-31 10:44 被阅读255次

    距离上一篇博客,已经过去很久了,还是要多写博客才行。那么接下来我将介绍如何将ffplay播放器移植到Android中。这里首先上GitHub地址:CainSDLPlayer

    开发环境介绍

    Android Studio 3.0.1
    FFmpeg-3.3.3
    SDL2.0.7

    简介

    FFmpeg编程开发笔记 —— ffplay.c 源码注释这篇文章中,我给ffplay的核心部分做了注释。如何通读过ffplay的源码,大家都会很清楚ffplay 的核心部分的构造是怎样的。这里借用一下雷博士博客中关于ffplay的架构图,大家可以去看雷霄骅博士的博客 ,这里非常感谢雷博士分享的资料,本人也从中获益良多。

    ffplay架构分析

    看到上面的架构,应该了解ffplay的整体框架构成了,ffplay是基于SDL实现的。所以我们想要移植ffplay的话,首先要将SDL移植到Android中。移植过程可以参考本人的文章 —— FFmpeg编程开发笔记 —— Android 移植 FFmpeg + SDL2.0 库。这里就不做过多的介绍了。

    移植过程

    这里,本人默认你已经成功将SDL2.0移植到了Android Studio当中。
    从上面的架构图以及ffplay的源码注释文章中可以了解到,ffplay 根据输入的文件名,创建解复用上下文,进入解复用线程read_thread的。在read_thread 方法中,查找媒体流AVStream,并根据设置调用stream_component_open打开相应的媒体流。之后进入解复用阶段,从文件中源源不断地读入AVPacket,存放到PacketQueue队列中,解码器又不断取出AVPacket进行解码,然后将解码得到的AVFrame存放到FrameQueue当中。所以这里我们首先将ffplay的头文件和定义结构体、PacketQueue 和 FrameQueue提取出来。

    ffplay_def.h:

    #ifndef CAINPLAYER_FFPLAYE_DEF_H
    #define CAINPLAYER_FFPLAYE_DEF_H
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include "ff_config.h"
    #include <inttypes.h>
    #include <math.h>
    #include <limits.h>
    #include <signal.h>
    #include <stdint.h>
    
    #include "libavutil/avstring.h"
    #include "libavutil/eval.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/pixdesc.h"
    #include "libavutil/imgutils.h"
    #include "libavutil/dict.h"
    #include "libavutil/parseutils.h"
    #include "libavutil/samplefmt.h"
    #include "libavutil/avassert.h"
    #include "libavutil/time.h"
    #include "libavformat/avformat.h"
    #include "libavdevice/avdevice.h"
    #include "libswscale/swscale.h"
    #include "libavutil/opt.h"
    #include "libavcodec/avfft.h"
    #include "libswresample/swresample.h"
    
    #include <SDL.h>
    #include "Mutex.h"
    #include "Thread.h"
    
    #define MAX_QUEUE_SIZE (15 * 1024 * 1024)
    #define MIN_FRAMES 25
    #define EXTERNAL_CLOCK_MIN_FRAMES 2
    #define EXTERNAL_CLOCK_MAX_FRAMES 10
    
    /* Minimum SDL audio buffer size, in samples. */
    #define SDL_AUDIO_MIN_BUFFER_SIZE 512
    /* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
    #define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30
    
    /* Step size for volume control in dB */
    #define SDL_VOLUME_STEP (0.75)
    
    /* no AV sync correction is done if below the minimum AV sync threshold */
    #define AV_SYNC_THRESHOLD_MIN 0.04
    /* AV sync correction is done if above the maximum AV sync threshold */
    #define AV_SYNC_THRESHOLD_MAX 0.1
    /* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
    #define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
    /* no AV correction is done if too big error */
    #define AV_NOSYNC_THRESHOLD 10.0
    
    /* maximum audio speed change to get correct sync */
    #define SAMPLE_CORRECTION_PERCENT_MAX 10
    
    /* external clock speed adjustment constants for realtime sources based on buffer fullness */
    #define EXTERNAL_CLOCK_SPEED_MIN  0.900
    #define EXTERNAL_CLOCK_SPEED_MAX  1.010
    #define EXTERNAL_CLOCK_SPEED_STEP 0.001
    
    /* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
    #define AUDIO_DIFF_AVG_NB   20
    
    /* polls for possible required screen refresh at least this often, should be less than 1/fps */
    #define REFRESH_RATE 0.01
    
    /* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
    /* TODO: We assume that a decoded and resampled frame fits into this buffer */
    #define SAMPLE_ARRAY_SIZE (8 * 65536)
    
    static unsigned sws_flags = SWS_BICUBIC;
    
    typedef struct MyAVPacketList {
       AVPacket pkt;
       struct MyAVPacketList *next;
       int serial;
    } MyAVPacketList;
    
    typedef struct PacketQueue {
       MyAVPacketList *first_pkt, *last_pkt;
       int nb_packets;
       int size;
       int64_t duration;
       int abort_request;
       int serial;
       Mutex *mutex;
       Cond *cond;
    } PacketQueue;
    
    #define VIDEO_PICTURE_QUEUE_SIZE 3
    #define SUBPICTURE_QUEUE_SIZE 16
    #define SAMPLE_QUEUE_SIZE 9
    #define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))
    
    typedef struct AudioParams {
        int freq;
        int channels;
        int64_t channel_layout;
        enum AVSampleFormat fmt;
        int frame_size;
        int bytes_per_sec;
    } AudioParams;
    
    typedef struct Clock {
        double pts;           /* clock base */
        double pts_drift;     /* clock base minus time at which we updated the clock */
        double last_updated;
        double speed;
        int serial;           /* clock is based on a packet with this serial */
        int paused;
        int *queue_serial;    /* pointer to the current packet queue serial, used for obsolete clock detection */
    } Clock;
    
    /* Common struct for handling all types of decoded data and allocated render buffers. */
    typedef struct Frame {
        AVFrame *frame;
        AVSubtitle sub;
        int serial;
        double pts;           /* presentation timestamp for the frame */
        double duration;      /* estimated duration of the frame */
        int64_t pos;          /* byte position of the frame in the input file */
        int width;
        int height;
        int format;
        AVRational sar;
        int uploaded;
        int flip_v;
    } Frame;
    
    typedef struct FrameQueue {
        Frame queue[FRAME_QUEUE_SIZE];
        int rindex;
        int windex;
        int size;
        int max_size;
        int keep_last;
        int rindex_shown;
        Mutex *mutex;
        Cond *cond;
        PacketQueue *pktq;
    } FrameQueue;
    
    enum {
        AV_SYNC_AUDIO_MASTER, /* default choice */
        AV_SYNC_VIDEO_MASTER,
        AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */
    };
    
    typedef struct Decoder {
        AVPacket pkt;
        AVPacket pkt_temp;
        PacketQueue *queue;
        AVCodecContext *avctx;
        int pkt_serial;
        int finished;
        int packet_pending;
        Cond *empty_queue_cond;
        int64_t start_pts;
        AVRational start_pts_tb;
        int64_t next_pts;
        AVRational next_pts_tb;
        Thread *decoder_tid;
    } Decoder;
    
    enum ShowMode {
        SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, SHOW_MODE_WAVES, SHOW_MODE_RDFT, SHOW_MODE_NB
    };
    
    typedef struct VideoState {
        int abort_request;          // 停止
        int force_refresh;          // 强制刷新
        int paused;                 // 暂停标志
        int last_paused;            // 上一次暂停状态标志
        int queue_attachments_req;  // 附着请求
        int seek_req;               // 定位请求
        int seek_flags;             // 定位标志
        int64_t seek_pos;           // 定位位置(秒)
        int64_t seek_rel;           //
        int read_pause_return;      // 上一次暂停回调
        AVFormatContext *ic;        // 解复用上下文
        int realtime;               // 是否实时流
    
        Clock audioClock;           // 音频时钟
        Clock videoClock;           // 视频时钟
        Clock extClock;             // 外部时钟
    
        FrameQueue videoFrameQueue; // 视频帧队列
        FrameQueue audioFrameQueue; // 音频帧队列
    
        Decoder audioDecoder;       // 音频解码器
        Decoder videoDecoder;       // 视频解码器
    
        int av_sync_type;           // 同步类型,默认是同步到音频
    
        int audioStreamIdx;         // 音频流索引
        AVStream *audioStream;      // 音频流
        PacketQueue audioQueue;     // 音频裸数据包队列
    
        int audio_volume;           // 音量大小
        int muted;                  // 是否静音
    
        double audio_clock;
        int audio_clock_serial;
        double audio_diff_cum; /* used for AV difference average computation */
        double audio_diff_avg_coef;
        double audio_diff_threshold;
        int audio_diff_avg_count;
        int audio_hw_buf_size;
        uint8_t *audio_buf;
        uint8_t *audio_buf1;
        unsigned int audio_buf_size; /* in bytes */
        unsigned int audio_buf1_size;
        int audio_buf_index; /* in bytes */
        int audio_write_buf_size;           // 写入大小
        struct AudioParams audio_src;
        struct AudioParams audio_tgt;
        struct SwrContext *swr_ctx;         // 音频转码上下文
    
        enum ShowMode show_mode;            // 显示模式,音频波形还是视频画面,这里仅支持视频画面
    
        SDL_Texture *vid_texture;           // 视频渲染Texture
    
    
        double frame_timer;                 // 帧计时器,记录当前帧的时钟
        double frame_last_filter_delay;     // 上一个滤镜延时
        int videoStreamIdx;                 // 视频流索引
        AVStream *videoStream;              // 视频流
        PacketQueue videoQueue;             // 视频裸数据包队列
        double max_frame_duration;          // 最大帧间隔
        struct SwsContext *img_convert_ctx; // 视频转码上下文
    
    
        int eof; // 结尾标志
        char *filename; // 文件名
        int width, height, xleft, ytop; // 宽高和起始位置等
        int step; // 跳帧
        Thread *readThread;       // 读文件线程
        Cond *readCondition;    // 读文件条件锁
    
        Thread *videRefreshThread;  // 画面刷新线程
    } VideoState;
    
    #ifdef __cplusplus
    };
    #endif
    
    #endif //CAINPLAYER_FFPLAYE_DEF_H
    

    在这里,你会发现。头文件中关于字幕和 AVFilter相关的东西我都删掉了,并且关于音频波形的texture也删掉了。我们首先只做音频流和视频流的播放器,了解播放器的本质(音频流和视频流的核心流程,而且Android手机环境下对AVFilter的处理还是交给OpenGLES来处理会比较好,不过这是后话了。这里暂时不讨论这么长远的东西,我们首先得得到一个能用的基于ffplay的播放器。

    PacketQueue.h:

    #ifndef CAINPLAYER_PACKETQUEUE_H
    #define CAINPLAYER_PACKETQUEUE_H
    
    #include "ffplay_def.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    // 入队裸数据包
    int packet_queue_put_private(PacketQueue *q, AVPacket *pkt);
    // 入队裸数据包
    int packet_queue_put(PacketQueue *q, AVPacket *pkt);
    // 入队空的裸数据包
    int packet_queue_put_nullpacket(PacketQueue *q, int stream_index);
    // 初始化队列
    int packet_queue_init(PacketQueue *q);
    // 刷出剩余裸数据包
    void packet_queue_flush(PacketQueue *q);
    // 销毁裸数据包队列
    void packet_queue_destroy(PacketQueue *q);
    // 停止入队
    void packet_queue_abort(PacketQueue *q);
    // 开始入队
    void packet_queue_start(PacketQueue *q);
    // 获取裸数据包
    int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial);
    
    #ifdef __cplusplus
    };
    #endif
    
    #endif //CAINPLAYER_PACKETQUEUE_H
    

    PacketQueue.cpp:

    #include "PacketQueue.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    extern AVPacket flush_pkt;
    
    /**
     * 入队裸数据包
     * @param q
     * @param pkt
     * @return
     */
    int packet_queue_put_private(PacketQueue *q, AVPacket *pkt) {
        MyAVPacketList *pkt1;
    
        if (q->abort_request)
            return -1;
    
        pkt1 = (MyAVPacketList *) av_malloc(sizeof(MyAVPacketList));
        if (!pkt1)
            return -1;
        pkt1->pkt = *pkt;
        pkt1->next = NULL;
        if (pkt == &flush_pkt)
            q->serial++;
        pkt1->serial = q->serial;
    
        if (!q->last_pkt)
            q->first_pkt = pkt1;
        else
            q->last_pkt->next = pkt1;
        q->last_pkt = pkt1;
        q->nb_packets++;
        q->size += pkt1->pkt.size + sizeof(*pkt1);
        q->duration += pkt1->pkt.duration;
        /* XXX: should duplicate packet data in DV case */
        CondSignal(q->cond);
        return 0;
    }
    
    /**
     * 入队裸数据包
     * @param q
     * @param pkt
     * @return
     */
    int packet_queue_put(PacketQueue *q, AVPacket *pkt) {
        int ret;
    
        MutexLock(q->mutex);
        ret = packet_queue_put_private(q, pkt);
        MutexUnlock(q->mutex);
    
        if (pkt != &flush_pkt && ret < 0)
            av_packet_unref(pkt);
    
        return ret;
    }
    
    /**
     * 入队空数据
     * @param q
     * @param stream_index
     * @return
     */
    int packet_queue_put_nullpacket(PacketQueue *q, int stream_index) {
        AVPacket pkt1, *pkt = &pkt1;
        av_init_packet(pkt);
        pkt->data = NULL;
        pkt->size = 0;
        pkt->stream_index = stream_index;
        return packet_queue_put(q, pkt);
    }
    
    /**
     * 初始化裸数据队列
     * @param q
     * @return
     */
    int packet_queue_init(PacketQueue *q) {
        memset(q, 0, sizeof(PacketQueue));
        q->mutex = MutexCreate();
        if (!q->mutex) {
            av_log(NULL, AV_LOG_FATAL, "MutexCreate(): %s\n", SDL_GetError());
            return AVERROR(ENOMEM);
        }
        q->cond = CondCreate();
        if (!q->cond) {
            av_log(NULL, AV_LOG_FATAL, "CondCreate(): %s\n", SDL_GetError());
            return AVERROR(ENOMEM);
        }
        q->abort_request = 1;
        return 0;
    }
    
    /**
     * 刷出剩余帧
     * @param q
     */
    void packet_queue_flush(PacketQueue *q) {
        MyAVPacketList *pkt, *pkt1;
    
        MutexLock(q->mutex);
        for (pkt = q->first_pkt; pkt; pkt = pkt1) {
            pkt1 = pkt->next;
            av_packet_unref(&pkt->pkt);
            av_freep(&pkt);
        }
        q->last_pkt = NULL;
        q->first_pkt = NULL;
        q->nb_packets = 0;
        q->size = 0;
        q->duration = 0;
        MutexUnlock(q->mutex);
    }
    
    /**
     * 销毁队列
     * @param q
     */
    void packet_queue_destroy(PacketQueue *q) {
        packet_queue_flush(q);
        MutexDestroy(q->mutex);
        CondDestroy(q->cond);
    }
    
    /**
     * 裸数据队列停止
     * @param q
     */
    void packet_queue_abort(PacketQueue *q) {
        MutexLock(q->mutex);
    
        q->abort_request = 1;
    
        CondSignal(q->cond);
    
        MutexUnlock(q->mutex);
    }
    
    /**
     * 裸数据包队列开始
     * @param q
     */
    void packet_queue_start(PacketQueue *q) {
        MutexLock(q->mutex);
        q->abort_request = 0;
        packet_queue_put_private(q, &flush_pkt);
        MutexUnlock(q->mutex);
    }
    
    /**
     * 取出裸数据包
     * @param q
     * @param pkt
     * @param block
     * @param serial
     * @return
     */
    int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial) {
        MyAVPacketList *pkt1;
        int ret;
    
        MutexLock(q->mutex);
    
        for (;;) {
            if (q->abort_request) {
                ret = -1;
                break;
            }
    
            pkt1 = q->first_pkt;
            if (pkt1) {
                q->first_pkt = pkt1->next;
                if (!q->first_pkt)
                    q->last_pkt = NULL;
                q->nb_packets--;
                q->size -= pkt1->pkt.size + sizeof(*pkt1);
                q->duration -= pkt1->pkt.duration;
                *pkt = pkt1->pkt;
                if (serial)
                    *serial = pkt1->serial;
                av_free(pkt1);
                ret = 1;
                break;
            } else if (!block) {
                ret = 0;
                break;
            } else {
                CondWait(q->cond, q->mutex);
            }
        }
        MutexUnlock(q->mutex);
        return ret;
    }
    
    #ifdef __cplusplus
    };
    #endif
    

    这里,PacketQueue 就是将ffplay中的对应方法移植过来,将原来的方法去掉static关键字。这么做只是为了后续方面将播放器的核心封装成C++,方便以后的音视频处理。
    FrameQueue.h:

    #ifndef CAINPLAYER_FRAMEQUEUE_H
    #define CAINPLAYER_FRAMEQUEUE_H
    
    #include "ffplay_def.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void frame_queue_unref_item(Frame *vp);
    int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last);
    void frame_queue_destory(FrameQueue *f);
    void frame_queue_signal(FrameQueue *f);
    Frame *frame_queue_peek(FrameQueue *f);
    Frame *frame_queue_peek_next(FrameQueue *f);
    Frame *frame_queue_peek_last(FrameQueue *f);
    Frame *frame_queue_peek_writable(FrameQueue *f);
    Frame *frame_queue_peek_readable(FrameQueue *f);
    void frame_queue_push(FrameQueue *f);
    void frame_queue_next(FrameQueue *f);
    int frame_queue_nb_remaining(FrameQueue *f);
    int64_t frame_queue_last_pos(FrameQueue *f);
    
    #ifdef __cplusplus
    };
    #endif
    
    #endif //CAINPLAYER_FRAMEQUEUE_H
    

    FrameQueue.cpp:

    #include "FrameQueue.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /**
     * 释放帧的引用
     * @param vp
     */
    void frame_queue_unref_item(Frame *vp) {
        av_frame_unref(vp->frame);
        avsubtitle_free(&vp->sub);
    }
    
    /**
     * 帧队列初始化
     * @param f
     * @param pktq
     * @param max_size
     * @param keep_last
     * @return
     */
    int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last) {
        int i;
        memset(f, 0, sizeof(FrameQueue));
        if (!(f->mutex = MutexCreate())) {
            av_log(NULL, AV_LOG_FATAL, "MutexCreate(): %s\n", SDL_GetError());
            return AVERROR(ENOMEM);
        }
        if (!(f->cond = CondCreate())) {
            av_log(NULL, AV_LOG_FATAL, "CondCreate(): %s\n", SDL_GetError());
            return AVERROR(ENOMEM);
        }
        f->pktq = pktq;
        f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
        f->keep_last = !!keep_last;
        for (i = 0; i < f->max_size; i++) {
            if (!(f->queue[i].frame = av_frame_alloc()))
                return AVERROR(ENOMEM);
        }
        return 0;
    }
    
    /**
     * 销毁帧队列
     * @param f
     */
    void frame_queue_destory(FrameQueue *f) {
        int i;
        for (i = 0; i < f->max_size; i++) {
            Frame *vp = &f->queue[i];
            frame_queue_unref_item(vp);
            av_frame_free(&vp->frame);
        }
        MutexDestroy(f->mutex);
        CondDestroy(f->cond);
    }
    
    /**
     * 通知
     * @param f
     */
    void frame_queue_signal(FrameQueue *f) {
        MutexLock(f->mutex);
        CondSignal(f->cond);
        MutexUnlock(f->mutex);
    }
    
    /**
     * 查找当前帧
     * @param f
     * @return
     */
    Frame *frame_queue_peek(FrameQueue *f) {
        return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
    }
    
    /**
     * 查找下一帧
     * @param f
     * @return
     */
    Frame *frame_queue_peek_next(FrameQueue *f) {
        return &f->queue[(f->rindex + f->rindex_shown + 1) % f->max_size];
    }
    
    /**
     * 查找上一帧
     * @param f
     * @return
     */
    Frame *frame_queue_peek_last(FrameQueue *f) {
        return &f->queue[f->rindex];
    }
    
    /**
     * 查找可写帧
     * @param f
     * @return
     */
    Frame *frame_queue_peek_writable(FrameQueue *f) {
    /* wait until we have space to put a new frame */
        MutexLock(f->mutex);
        while (f->size >= f->max_size &&
               !f->pktq->abort_request) {
            CondWait(f->cond, f->mutex);
        }
        MutexUnlock(f->mutex);
    
        if (f->pktq->abort_request) {
            return NULL;
        }
    
        return &f->queue[f->windex];
    }
    
    /**
     * 查找可读帧
     * @param f
     * @return
     */
    Frame *frame_queue_peek_readable(FrameQueue *f) {
    /* wait until we have a readable a new frame */
        MutexLock(f->mutex);
        while (f->size - f->rindex_shown <= 0 &&
               !f->pktq->abort_request) {
            CondWait(f->cond, f->mutex);
        }
        MutexUnlock(f->mutex);
    
        if (f->pktq->abort_request) {
            return NULL;
        }
    
        return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
    }
    
    /**
     * 入队
     * @param f
     */
    void frame_queue_push(FrameQueue *f) {
        if (++f->windex == f->max_size) {
            f->windex = 0;
        }
        MutexLock(f->mutex);
        f->size++;
        CondSignal(f->cond);
        MutexUnlock(f->mutex);
    }
    
    /**
     * 下一帧
     * @param f
     */
    void frame_queue_next(FrameQueue *f) {
        if (f->keep_last && !f->rindex_shown) {
            f->rindex_shown = 1;
            return;
        }
        frame_queue_unref_item(&f->queue[f->rindex]);
        if (++f->rindex == f->max_size) {
            f->rindex = 0;
        }
        MutexLock(f->mutex);
        f->size--;
        CondSignal(f->cond);
        MutexUnlock(f->mutex);
    }
    
    /**
     * 剩余帧
     * @param f
     * @return
     */
    int frame_queue_nb_remaining(FrameQueue *f) {
        return f->size - f->rindex_shown;
    }
    
    /**
     * 上一个位置
     * @param f
     * @return
     */
    int64_t frame_queue_last_pos(FrameQueue *f) {
        Frame *fp = &f->queue[f->rindex];
        if (f->rindex_shown && fp->serial == f->pktq->serial) {
            return fp->pos;
        } else {
            return -1;
        }
    }
    
    #ifdef __cplusplus
    };
    #endif
    

    到这里,我们就得到了PacketQueue 和FrameQueue 文件,这些文件是独立出来的,跟播放器关联不大。好了接下来我们将解码器和同步时钟相关的方法也提取出来:
    MediaDecoder.h:

    #ifndef CAINPLAYER_MEDIADECODER_H
    #define CAINPLAYER_MEDIADECODER_H
    
    #include "ffplay_def.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    // 解码器初始化
    void decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, Cond *empty_queue_cond);
    // 解码器解码帧
    int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub);
    // 销毁解码器
    void decoder_destroy(Decoder *d);
    // 解码器开始解码
    int decoder_start(Decoder *d, int (*fn)(void *), void *arg);
    // 解码器取消解码
    void decoder_abort(Decoder *d, FrameQueue *fq);
    
    #ifdef __cplusplus
    };
    #endif
    
    #endif //CAINPLAYER_MEDIADECODER_H
    

    MediaDecoder.cpp:

    #include "MediaDecoder.h"
    #include "PacketQueue.h"
    #include "FrameQueue.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    extern AVPacket flush_pkt;
    /**
     * 解码器初始化
     * @param d
     * @param avctx
     * @param queue
     * @param empty_queue_cond
     */
    void decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, Cond *empty_queue_cond) {
        memset(d, 0, sizeof(Decoder));
        d->avctx = avctx;
        d->queue = queue;
        d->empty_queue_cond = empty_queue_cond;
        d->start_pts = AV_NOPTS_VALUE;
    }
    
    /**
     * 解码器解码帧
     * @param d
     * @param frame
     * @param sub
     * @return
     */
    int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
        int got_frame = 0;
    
        do {
            int ret = -1;
    
            if (d->queue->abort_request) {
                return -1;
            }
    
            if (!d->packet_pending || d->queue->serial != d->pkt_serial) {
                AVPacket pkt;
                do {
                    if (d->queue->nb_packets == 0)
                        CondSignal(d->empty_queue_cond);
                    if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0)
                        return -1;
                    if (pkt.data == flush_pkt.data) {
                        avcodec_flush_buffers(d->avctx);
                        d->finished = 0;
                        d->next_pts = d->start_pts;
                        d->next_pts_tb = d->start_pts_tb;
                    }
                } while (pkt.data == flush_pkt.data || d->queue->serial != d->pkt_serial);
                av_packet_unref(&d->pkt);
                d->pkt_temp = d->pkt = pkt;
                d->packet_pending = 1;
            }
    
            switch (d->avctx->codec_type) {
                case AVMEDIA_TYPE_VIDEO:
                    ret = avcodec_decode_video2(d->avctx, frame, &got_frame, &d->pkt_temp);
                    if (got_frame) {
    //                    if (decoder_reorder_pts == -1) {
    //                        frame->pts = av_frame_get_best_effort_timestamp(frame);
    //                    } else if (!decoder_reorder_pts) {
    //                        frame->pts = frame->pkt_dts;
    //                    }
                        frame->pts = av_frame_get_best_effort_timestamp(frame);
                    }
                    break;
    
                case AVMEDIA_TYPE_AUDIO:
                    ret = avcodec_decode_audio4(d->avctx, frame, &got_frame, &d->pkt_temp);
                    if (got_frame) {
                        AVRational tb = (AVRational){1, frame->sample_rate};
                        if (frame->pts != AV_NOPTS_VALUE)
                            frame->pts = av_rescale_q(frame->pts, av_codec_get_pkt_timebase(d->avctx), tb);
                        else if (d->next_pts != AV_NOPTS_VALUE)
                            frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
                        if (frame->pts != AV_NOPTS_VALUE) {
                            d->next_pts = frame->pts + frame->nb_samples;
                            d->next_pts_tb = tb;
                        }
                    }
                    break;
    
                case AVMEDIA_TYPE_SUBTITLE:
                    ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, &d->pkt_temp);
                    break;
            }
    
            if (ret < 0) {
                d->packet_pending = 0;
            } else {
                d->pkt_temp.dts =
                d->pkt_temp.pts = AV_NOPTS_VALUE;
                if (d->pkt_temp.data) {
                    if (d->avctx->codec_type != AVMEDIA_TYPE_AUDIO)
                        ret = d->pkt_temp.size;
                    d->pkt_temp.data += ret;
                    d->pkt_temp.size -= ret;
                    if (d->pkt_temp.size <= 0)
                        d->packet_pending = 0;
                } else {
                    if (!got_frame) {
                        d->packet_pending = 0;
                        d->finished = d->pkt_serial;
                    }
                }
            }
        } while (!got_frame && !d->finished);
    
        return got_frame;
    }
    
    /**
     * 销毁解码器
     * @param d
     */
    void decoder_destroy(Decoder *d) {
        av_packet_unref(&d->pkt);
        avcodec_free_context(&d->avctx);
    }
    
    /**
     * 解码器开始解码
     * @param d
     * @param fn
     * @param arg
     * @return
     */
    int decoder_start(Decoder *d, int (*fn)(void *), void *arg) {
        packet_queue_start(d->queue);
        d->decoder_tid = ThreadCreate(fn, arg, "decoder");
        if (!d->decoder_tid) {
            av_log(NULL, AV_LOG_ERROR, "ThreadCreate(): %s\n", SDL_GetError());
            return AVERROR(ENOMEM);
        }
        return 0;
    }
    
    /**
     * 解码器取消解码
     * @param d
     * @param fq
     */
    void decoder_abort(Decoder *d, FrameQueue *fq) {
        packet_queue_abort(d->queue);
        frame_queue_signal(fq);
        ThreadWait(d->decoder_tid, NULL);
        d->decoder_tid = NULL;
        packet_queue_flush(d->queue);
    }
    
    #ifdef __cplusplus
    };
    #endif
    

    解码器部分,依赖了 flush_pkt 和 decoder_reorder_pts。其中decoder_reorder_pts 是用于重排pts 的,这个参数,我们默认是重排,所以将ffplay的decoder_decode_frame方法中的判断注释掉:

    //                    if (decoder_reorder_pts == -1) {
    //                        frame->pts = av_frame_get_best_effort_timestamp(frame);
    //                    } else if (!decoder_reorder_pts) {
    //                        frame->pts = frame->pkt_dts;
    //                    }
                        frame->pts = av_frame_get_best_effort_timestamp(frame);
    

    Clock.h:

    #ifndef CAINPLAYER_CLOCK_H
    #define CAINPLAYER_CLOCK_H
    
    #include "ffplay_def.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    // 获取时钟
    double get_clock(Clock *c);
    
    // 设置时钟
    void set_clock_at(Clock *c, double pts, int serial, double time);
    // 设置时钟
    void set_clock(Clock *c, double pts, int serial);
    
    // 设置时钟速度
    void set_clock_speed(Clock *c, double speed);
    // 初始化时钟
    void init_clock(Clock *c, int *queue_serial);
    // 同步到从属时钟
    void sync_clock_to_slave(Clock *c, Clock *slave);
    
    #ifdef __cplusplus
    };
    #endif
    
    #endif //CAINPLAYER_CLOCK_H
    

    Clock.cpp:

    #include "Clock.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /**
     * 获取时钟
     * @param c
     * @return
     */
    double get_clock(Clock *c) {
        if (*c->queue_serial != c->serial) {
            return NAN;
        }
        if (c->paused) {
            return c->pts;
        } else {
            double time = av_gettime_relative() / 1000000.0;
            return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
        }
    }
    
    /**
     * 设置时钟
     * @param c
     * @param pts
     * @param serial
     * @param time
     */
    void set_clock_at(Clock *c, double pts, int serial, double time) {
        c->pts = pts;
        c->last_updated = time;
        c->pts_drift = c->pts - time;
        c->serial = serial;
    }
    
    /**
     * 设置时钟
     * @param c
     * @param pts
     * @param serial
     */
    void set_clock(Clock *c, double pts, int serial) {
        double time = av_gettime_relative() / 1000000.0;
        set_clock_at(c, pts, serial, time);
    }
    
    /**
     * 设置时钟速度
     * @param c
     * @param speed
     */
    void set_clock_speed(Clock *c, double speed) {
        set_clock(c, get_clock(c), c->serial);
        c->speed = speed;
    }
    
    /**
     * 初始化时钟
     * @param c
     * @param queue_serial
     */
    void init_clock(Clock *c, int *queue_serial) {
        c->speed = 1.0;
        c->paused = 0;
        c->queue_serial = queue_serial;
        set_clock(c, NAN, -1);
    }
    
    /**
     * 同步到从属时钟
     * @param c
     * @param slave
     */
    void sync_clock_to_slave(Clock *c, Clock *slave) {
        double clock = get_clock(c);
        double slave_clock = get_clock(slave);
        if (!isnan(slave_clock) && (isnan(clock) || fabs(clock - slave_clock) > AV_NOSYNC_THRESHOLD)) {
            set_clock(c, slave_clock, slave->serial);
        }
    }
    
    #ifdef __cplusplus
    };
    #endif
    

    到这里,我们又将解码器和同步时钟的方法抽取出来了。
    接下来,我们用创造锁和线程,用于替换掉SDL中的锁和线程:
    Thread.h:

    #ifndef CAINCAMERA_THREAD_H
    #define CAINCAMERA_THREAD_H
    
    #ifdef __cplusplus
    extern "C" {
    
    #endif
    
    #include "Mutex.h"
    
    // 线程优先级
    typedef enum {
        THREAD_PRIORITY_LOW,
        THREAD_PRIORITY_NORMAL,
        THREAD_PRIORITY_HIGH
    } ThreadPriority;
    
    // 线程结构
    typedef struct Thread
    {
        pthread_t id;
        int (*func)(void *);
        void *data;
        char name[32];
        int retval;
    } Thread;
    
    
    // 创建线程
    Thread *ThreadCreate(int (*fun)(void *), void *data, const char *name);
    // 设置线程优先级
    int ThreadSetPriority(ThreadPriority priority);
    // 等待线程
    void ThreadWait(Thread *thread, int *status);
    // 解绑线程
    void ThreadDetach(Thread *thread);
    
    // 销毁线程
    void ThreadDestroy(Thread *thread);
    
    #ifdef __cplusplus
    };
    #endif
    
    #endif //CAINCAMERA_THREAD_H
    

    Thread.cpp:

    #include <unistd.h>
    #include <malloc.h>
    #include "native_log.h"
    #include "Thread.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <errno.h>
    #include <assert.h>
    
    /**
     * 线程函数
     * @param data
     * @return
     */
    static void *ThreadRun(void *data) {
        Thread *thread = (Thread *) data;
        ALOGI("ThreadRun: [%d] %s\n", (int) gettid(), thread->name);
        pthread_setname_np(pthread_self(), thread->name);
        thread->retval = thread->func(thread->data);
        ThreadDetach(thread);
        return NULL;
    }
    
    /**
     * 创建线程
     * @param fn
     * @param data
     * @param name
     * @return
     */
    Thread *ThreadCreate(int (*fun)(void *), void *data, const char *name) {
    
        Thread *thread = (Thread *) malloc(sizeof(Thread));
        thread->func = fun;
        thread->data = data;
        strlcpy(thread->name, name, sizeof(thread->name) - 1);
        // 创建线程
        int retval = pthread_create(&thread->id, NULL, ThreadRun, thread);
        if (retval) {
            return NULL;
        }
        return thread;
    }
    
    
    /**
     * 设置线程优先级
     * @param priority
     * @return
     */
    int ThreadSetPriority(ThreadPriority priority) {
        struct sched_param sched;
        int policy;
        // 获取当前线程
        pthread_t thread = pthread_self();
        // 获取线程优先级参数
        if (pthread_getschedparam(thread, &policy, &sched) < 0) {
            ALOGE("call pthread_getschedparam() failed!\n");
            return -1;
        }
        if (priority == THREAD_PRIORITY_LOW) {
            sched.sched_priority = sched_get_priority_min(policy);
        } else if (priority == THREAD_PRIORITY_HIGH) {
            sched.sched_priority = sched_get_priority_max(policy);
        } else {
            int min_priority = sched_get_priority_min(policy);
            int max_priority = sched_get_priority_max(policy);
            sched.sched_priority = (min_priority + (max_priority - min_priority) / 2);
        }
    
        // 设置线程优先级
        if (pthread_setschedparam(thread, policy, &sched) < 0) {
            ALOGE("call pthread_setschedparam() failed");
            return -1;
        }
        return 0;
    }
    
    /**
     * 等待线程
     * @param thread
     * @param status
     */
    void ThreadWait(Thread *thread, int *status) {
        assert(thread);
        if (!thread) {
            return;
        }
        // 等待线程结束
        pthread_join(thread->id, NULL);
        if (status) {
            *status = thread->retval;
        }
    }
    
    /**
     * 解绑线程
     * @param thread
     */
    void ThreadDetach(Thread *thread) {
        assert(thread);
        if (!thread) {
            return;
        }
        // 解绑线程
        pthread_detach(thread->id);
    }
    
    /**
     * 销毁线程
     * @param thread
     */
    void ThreadDestroy(Thread * thread) {
        if (thread) {
            ThreadWait(thread, NULL);
            free(thread);
            thread = NULL;
        }
    }
    
    #ifdef __cplusplus
    }
    #endif
    

    Mutex.h:

    #ifndef CAINCAMERA_MUTEX_H
    #define CAINCAMERA_MUTEX_H
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <pthread.h>
    #include <stdint.h>
    
    #define MUTEX_TIMEDOUT  1
    #define MUTEX_MAXWAIT   (~(uint32_t)0)
    
    // 互斥锁结构
    typedef struct Mutex {
        pthread_mutex_t id;
    } Mutex;
    
    // 创建互斥锁
    Mutex *MutexCreate(void);
    // 销毁互斥锁
    void MutexDestroy(Mutex *mutex);
    // 销毁互斥锁指针
    void MutexDestroyPointer(Mutex **pMutex);
    // 上锁
    int MutexLock(Mutex *mutex);
    // 解锁
    int MutexUnlock(Mutex *mutex);
    
    // 条件锁结构
    typedef struct Cond {
        pthread_cond_t id;
    } Cond;
    
    // 创建条件锁
    Cond *CondCreate(void);
    // 销毁条件锁
    void CondDestroy(Cond *cond);
    // 销毁条件锁指针
    void CondDestroyPointer(Cond **pCond);
    // 条件锁信号
    int CondSignal(Cond *cond);
    // 条件锁广播
    int CondBroadcast(Cond *cond);
    // 等待条件锁
    int CondWait(Cond *cond, Mutex *mutex);
    // 等待条件锁多少秒
    int CondWaitTimeout(Cond *cond, Mutex *mutex, uint32_t msec);
    
    // 出错信息
    const char *GetError(void);
    
    #ifdef __cplusplus
    }
    #endif
    #endif //CAINCAMERA_MUTEX_H
    

    Mutex.cpp:

    #include "Mutex.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <errno.h>
    #include <assert.h>
    #include <sys/time.h>
    #include <stdlib.h>
    #include <memory.h>
    
    /**
     * 创建互斥锁
     * @return
     */
    Mutex *MutexCreate(void) {
        Mutex *mutex;
        mutex = (Mutex *) malloc(sizeof(Mutex));
        if (!mutex) {
            return NULL;
        }
        memset(mutex, 0, sizeof(Mutex));
        if (pthread_mutex_init(&mutex->id, NULL) != 0) {
            free(mutex);
            return NULL;
        }
        return mutex;
    }
    
    /**
     * 销毁互斥锁
     * @param mutex
     */
    void MutexDestroy(Mutex *mutex) {
        if (mutex) {
            pthread_mutex_destroy(&mutex->id);
            free(mutex);
        }
    }
    
    /**
     * 销毁互斥锁指针
     * @param mutex
     */
    void MutexDestroyPointer(Mutex **mutex) {
        if (mutex) {
            MutexDestroy(*mutex);
            *mutex = NULL;
        }
    }
    
    /**
     * 上互斥锁
     * @param mutex
     * @return
     */
    int MutexLock(Mutex *mutex) {
        assert(mutex);
        if (!mutex) {
            return -1;
        }
        return pthread_mutex_lock(&mutex->id);
    }
    
    /**
     * 解除互斥锁
     * @param mutex
     * @return
     */
    int MutexUnlock(Mutex *mutex) {
        assert(mutex);
        if (!mutex) {
            return -1;
        }
        return pthread_mutex_unlock(&mutex->id);
    }
    
    
    
    /**
     * 创建条件锁
     * @return
     */
    Cond *CondCreate(void) {
        Cond *cond;
        cond = (Cond *) malloc(sizeof(Cond));
        if (!cond) {
            return NULL;
        }
        memset(cond, 0, sizeof(Cond));
        if (pthread_cond_init(&cond->id, NULL) != 0) {
            free(cond);
            return NULL;
        }
        return cond;
    }
    
    
    /**
     * 销毁条件锁
     * @param cond
     */
    void CondDestroy(Cond *cond) {
        if (cond) {
            pthread_cond_destroy(&cond->id);
            free(cond);
        }
    }
    
    /**
     * 销毁条件锁指针
     * @param cond
     */
    void CondDestroyPointer(Cond **cond) {
        if (cond) {
            CondDestroy(*cond);
            *cond = NULL;
        }
    }
    
    /**
     * 条件锁信号
     * @param cond
     * @return
     */
    int CondSignal(Cond *cond) {
        assert(cond);
        if (!cond) {
            return -1;
        }
        return pthread_cond_signal(&cond->id);
    }
    
    /**
     * 条件锁广播,用于唤醒多个条件变量
     * @param cond
     * @return
     */
    int CondBroadcast(Cond *cond) {
        assert(cond);
        if (!cond) {
            return -1;
        }
        return pthread_cond_broadcast(&cond->id);
    }
    
    /**
     * 等待条件锁
     * @param cond
     * @param mutex
     * @return
     */
    int CondWait(Cond *cond, Mutex *mutex) {
        assert(cond);
        assert(mutex);
        if (!cond || !mutex) {
            return -1;
        }
    
        return pthread_cond_wait(&cond->id, &mutex->id);
    }
    
    /**
     * 等待条件锁多少秒
     * @param cond
     * @param mutex
     * @param ms
     * @return
     */
    int CondWaitTimeout(Cond *cond, Mutex *mutex, uint32_t ms) {
        int retval;
        struct timeval delta;
        struct timespec abstime;
    
        assert(cond);
        assert(mutex);
        if (!cond || !mutex) {
            return -1;
        }
    
        gettimeofday(&delta, NULL);
    
        abstime.tv_sec = delta.tv_sec + (time_t)(ms / 1000);
        abstime.tv_nsec = (time_t) (delta.tv_usec + (ms % 1000) * 1000) * 1000;
        if (abstime.tv_nsec > 1000000000) {
            abstime.tv_sec++;
            abstime.tv_nsec -= 1000000000;
        }
    
        while (1) {
            retval = pthread_cond_timedwait(&cond->id, &mutex->id, &abstime);
            if (retval == 0) {
                return 0;
            } else if (retval == EINTR) {
                continue;
            } else if (retval == ETIMEDOUT) {
                return MUTEX_TIMEDOUT;
            } else {
                break;
            }
        }
        return -1;
    }
    
    /**
     * 出错信息
     * @return
     */
    const char *GetError(void) {
        return NULL;
    }
    
    #ifdef __cplusplus
    }
    #endif
    

    至此,我们将ffplay的很多独立的方法移植过来了。那么接下来,我们开始造播放器了:
    CainPlayer.h:

    #ifndef CAINPLAYER_CAINPLAYER_H
    #define CAINPLAYER_CAINPLAYER_H
    
    #include "ffplay_def.h"
    
    class CainPlayer {
    
    public:
        // 刷新画面用的方法
        int realloc_texture(SDL_Texture **texture, Uint32 new_format, int new_width, int new_height, SDL_BlendMode blendmode, int init_texture);
        // 计算显示的区域
        void calculate_display_rect(SDL_Rect *rect,
                                    int scr_xleft, int scr_ytop, int scr_width, int scr_height,
                                    int pic_width, int pic_height, AVRational pic_sar);
        // 将视频帧数据上载到Texture中
        int upload_texture(SDL_Texture *tex, AVFrame *frame, struct SwsContext **img_convert_ctx);
        // 显示画面
        void video_image_display(VideoState *is);
    
        // 打开媒体流
        int stream_component_open(VideoState *is, int stream_index);
        // 打开文件
        VideoState *stream_open(const char *filename);
        // 关闭媒体流
        void stream_component_close(VideoState *is, int stream_index);
        // 关闭媒体流
        void stream_close(VideoState *is);
    
        // 退出播放器
        void exitPlayer();
        // 退出播放器
        void do_exit(VideoState *is);
        // 回调
        static void sigterm_handler(int sig);
        // 计算大小
        void set_default_window_size(int width, int height, AVRational sar);
        //  打开视频,创建渲染器用的
        int video_open(VideoState *is);
        // 视频显示
        void video_display(VideoState *is);
    
        // 同步类型
        int get_master_sync_type(VideoState *is);
        // 获取主时钟
        double get_master_clock(VideoState *is);
        // 检查外部时钟速度
        void check_external_clock_speed(VideoState *is);
    
        // 定位操作
        void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes);
        // 暂停/播放
        void stream_toggle_pause(VideoState *is);
        // 暂停/播放
        void toggle_pause(VideoState *is);
        // 是否静音
        void toggle_mute(VideoState *is);
        // 更新音量
        void update_volume(VideoState *is, int sign, double step);
        // 跳转下一帧
        void step_to_next_frame(VideoState *is);
    
        // 计算延时
        double compute_target_delay(double delay, VideoState *is);
        // 计算显示间隔
        double vp_duration(VideoState *is, Frame *vp, Frame *nextvp);
        // 更新pts
        void update_video_pts(VideoState *is, double pts, int64_t pos, int serial);
        // 刷新视频画面
        void video_refresh(void *opaque, double *remaining_time);
    
        // 将解码后的视频帧入队
        int queue_picture(VideoState *is, AVFrame *src_frame, double pts, double duration, int64_t pos, int serial);
        // 获取视频帧
        int get_video_frame(VideoState *is, AVFrame *frame);
    
        // 线程
        static int read_thread(void *arg);
        static int audio_thread(void *arg);
        static int video_thread(void *arg);
    
        int demux();
        int audioDecode();
        int videoDecode();
    
        // 同步音频
        int synchronize_audio(VideoState *is, int nb_samples);
        // 音频解码
        int audio_decode_frame(VideoState *is);
        // 音频回调
        static void sdl_audio_callback(void *opaque, Uint8 *stream, int len);
        // 音频回调
        void audio_callback(Uint8 *stream, int len);
        // 打开音频设备
        int audio_open(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params);
    
        // 解码中断回调
        static int decode_interrupt_cb(void *ctx);
        // 判断媒体流中是否存在足够的裸数据包
        int stream_has_enough_packets(AVStream *st, int stream_id, PacketQueue *queue);
        // 是否实时流
        static int is_realtime(AVFormatContext *s);
    
    
        // 视频刷新线程
        static int video_refresh_thread(void *arg);
        void refreshVideo(void);
    
        // 刷新事件画面
        void event_loop(VideoState *cur_stream);
        static int lockmgr(void **mtx, enum AVLockOp op);
    
        // 播放方法(main)
        int play(int argc, char **argv);
    
    private:
    
        VideoState *is;
    
        AVDictionary *sws_dict;
        AVDictionary *swr_opts;
        AVDictionary *format_opts, *codec_opts, *resample_opts;
    
        int default_width  = 640;
        int default_height = 480;
        int screen_width  = 1080;
        int screen_height = 1920;
        int frameWidth;     // 帧的宽度,用于判断是否需要重新创建Texture
        int frameHeight;    // 帧的高度,用于判断是否需要重新创建Texture
    
        int audio_disable;                          // 禁用音频流
        int video_disable;                          // 禁用视频流
        const char* wanted_stream_spec[AVMEDIA_TYPE_NB] = {0};
        int seek_by_bytes = -1;                     // 以字节方式定位,用于ogg等格式
        int display_disable;                        // 禁用显示
        int startup_volume = 100;                   // 初始音频
        int av_sync_type = AV_SYNC_AUDIO_MASTER;    // 音视频同步方式,默认是同步到音频
        int64_t start_time = AV_NOPTS_VALUE;        // 开始播放的时间
        int64_t duration = AV_NOPTS_VALUE;          // 时长
        int fast = 0;
        int genpts = 0;
        int lowres = 0;
    
        int autoexit;                               // 播放结束自动退出
        int loop = 1;                               // 循环播放
        int framedrop = -1;                         // 舍帧操作,如果音频远比视频超前,则舍帧
        int infinite_buffer = -1;                   // 无限缓冲区,用于流媒体缓存
        enum ShowMode show_mode = SHOW_MODE_NONE;   // 显示模式
    
        /* current context */
        int64_t audio_callback_time; // 音频回调时间
    
    #define FF_QUIT_EVENT    (SDL_USEREVENT + 2)
    
        SDL_Window *window;
        SDL_Renderer *renderer;
    
    };
    #endif //CAINPLAYER_CAINPLAYER_H
    

    这里,我们将ffplay中的核心方法放到CainPlayer类里面,又将本来定义在ffplay文件头部的一些参数用作CainPlayer 的变量。接下来,我们将核心的方法移植过来。本人在移植过程中,将AVFilter 和 字幕相关的方法都已经删掉了。并且,SDL的锁和线程都用刚才实现的Mutex 和 Thread 替换掉了。是为了以后可以将整个SDL 替换掉,逐步向ijkplayer 的方向靠拢。
    CainPlayer.cpp:

    #include "CainPlayer.h"
    
    #include "PacketQueue.h"
    #include "FrameQueue.h"
    #include "MediaDecoder.h"
    #include "Clock.h"
    #include "CmdUtils.h"
    
    AVPacket flush_pkt;
    
    /**
     * 重新创建Texture
     * @param texture
     * @param new_format
     * @param new_width
     * @param new_height
     * @param blendmode
     * @param init_texture
     * @return
     */
    int CainPlayer::realloc_texture(SDL_Texture **texture, Uint32 new_format, int new_width, int new_height, SDL_BlendMode blendmode, int init_texture) {
        Uint32 format;
        if (new_width != frameWidth || new_height != frameHeight || new_format != format) {
            frameWidth = new_width;
            frameHeight = new_height;
            void *pixels;
            int pitch;
            if (*texture != NULL) {
                SDL_DestroyTexture(*texture);
            }
            if (!(*texture = SDL_CreateTexture(renderer, new_format, SDL_TEXTUREACCESS_STREAMING, new_width, new_height))) {
                return -1;
            }
            if (SDL_SetTextureBlendMode(*texture, blendmode) < 0) {
                return -1;
            }
            if (init_texture) {
                if (SDL_LockTexture(*texture, NULL, &pixels, &pitch) < 0) {
                    return -1;
                }
                memset(pixels, 0, pitch * new_height);
                SDL_UnlockTexture(*texture);
            }
        }
        return 0;
    }
    
    /**
     * 计算显示区域
     * @param rect
     * @param scr_xleft
     * @param scr_ytop
     * @param scr_width
     * @param scr_height
     * @param pic_width
     * @param pic_height
     * @param pic_sar
     */
    void CainPlayer::calculate_display_rect(SDL_Rect *rect,
                                int scr_xleft, int scr_ytop, int scr_width, int scr_height,
                                int pic_width, int pic_height, AVRational pic_sar) {
        float aspect_ratio;
        int width, height, x, y;
    
        if (pic_sar.num == 0) {
            aspect_ratio = 0;
        } else {
            aspect_ratio = av_q2d(pic_sar);
        }
        if (aspect_ratio <= 0.0) {
            aspect_ratio = 1.0;
        }
        aspect_ratio *= (float)pic_width / (float)pic_height;
    
        /* XXX: we suppose the screen has a 1.0 pixel ratio */
        height = scr_height;
        width = lrint(height * aspect_ratio) & ~1;
        if (width > scr_width) {
            width = scr_width;
            height = lrint(width / aspect_ratio) & ~1;
        }
        x = (scr_width - width) / 2;
        y = (scr_height - height) / 2;
        rect->x = scr_xleft + x;
        rect->y = scr_ytop  + y;
        rect->w = FFMAX(width,  1);
        rect->h = FFMAX(height, 1);
    }
    
    /**
     * 上载texture
     * @param tex
     * @param frame
     * @param img_convert_ctx
     * @return
     */
    int CainPlayer::upload_texture(SDL_Texture *tex, AVFrame *frame, struct SwsContext **img_convert_ctx) {
        int ret = 0;
        switch (frame->format) {
            case AV_PIX_FMT_YUV420P:
                if (frame->linesize[0] < 0 || frame->linesize[1] < 0 || frame->linesize[2] < 0) {
                    av_log(NULL, AV_LOG_ERROR, "Negative linesize is not supported for YUV.\n");
                    return -1;
                }
                ret = SDL_UpdateYUVTexture(tex, NULL, frame->data[0], frame->linesize[0],
                                           frame->data[1], frame->linesize[1],
                                           frame->data[2], frame->linesize[2]);
                break;
            case AV_PIX_FMT_BGRA:
                if (frame->linesize[0] < 0) {
                    ret = SDL_UpdateTexture(tex, NULL, frame->data[0] + frame->linesize[0] * (frame->height - 1), -frame->linesize[0]);
                } else {
                    ret = SDL_UpdateTexture(tex, NULL, frame->data[0], frame->linesize[0]);
                }
                break;
            default:
                /* This should only happen if we are not using avfilter... */
                *img_convert_ctx = sws_getCachedContext(*img_convert_ctx,
                                                        frame->width, frame->height,
                                                        (AVPixelFormat) frame->format, frame->width, frame->height,
                                                        AV_PIX_FMT_BGRA, sws_flags, NULL, NULL, NULL);
                if (*img_convert_ctx != NULL) {
                    uint8_t *pixels[4];
                    int pitch[4];
                    if (!SDL_LockTexture(tex, NULL, (void **)pixels, pitch)) {
                        sws_scale(*img_convert_ctx, (const uint8_t * const *)frame->data, frame->linesize,
                                  0, frame->height, pixels, pitch);
                        SDL_UnlockTexture(tex);
                    }
                } else {
                    av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
                    ret = -1;
                }
                break;
        }
        return ret;
    }
    
    /**
     * 显示视频
     * @param is
     */
    void CainPlayer::video_image_display(VideoState *is) {
        Frame *vp;
        SDL_Rect rect;
    
        vp = frame_queue_peek_last(&is->videoFrameQueue);
    
        calculate_display_rect(&rect, is->xleft, is->ytop, is->width, is->height, vp->width, vp->height, vp->sar);
    
        if (!vp->uploaded) {
            int sdl_pix_fmt = vp->frame->format == AV_PIX_FMT_YUV420P ? SDL_PIXELFORMAT_YV12 : SDL_PIXELFORMAT_ARGB8888;
            if (realloc_texture(&is->vid_texture, sdl_pix_fmt, vp->frame->width, vp->frame->height, SDL_BLENDMODE_NONE, 0) < 0)
                return;
            if (upload_texture(is->vid_texture, vp->frame, &is->img_convert_ctx) < 0)
                return;
            vp->uploaded = 1;
            vp->flip_v = vp->frame->linesize[0] < 0;
        }
    
        SDL_RenderCopyEx(renderer, is->vid_texture, NULL, &rect, 0, NULL,
                         (const SDL_RendererFlip) (vp->flip_v ? SDL_FLIP_VERTICAL : 0));
    }
    
    /**
     * 打开媒体流
     * @param is
     * @param stream_index
     * @return
     */
    int CainPlayer::stream_component_open(VideoState *is, int stream_index) {
        AVFormatContext *ic = is->ic;
        AVCodecContext *avctx;
        AVCodec *codec;
        AVDictionary *opts = NULL;
        AVDictionaryEntry *t = NULL;
        int sample_rate, nb_channels;
        int64_t channel_layout;
        int ret = 0;
        int stream_lowres = lowres;
    
        if (stream_index < 0 || stream_index >= ic->nb_streams) {
            return -1;
        }
    
        // 创建解码上下文
        avctx = avcodec_alloc_context3(NULL);
        if (!avctx) {
            return AVERROR(ENOMEM);
        }
    
        // 复制上下文参数
        ret = avcodec_parameters_to_context(avctx, ic->streams[stream_index]->codecpar);
        if (ret < 0) {
            goto fail;
        }
    
        // 设定时钟基准
        av_codec_set_pkt_timebase(avctx, ic->streams[stream_index]->time_base);
    
        // 创建解码器
        codec = avcodec_find_decoder(avctx->codec_id);
        // 判断是否成功得到解码器
        if (!codec) {
            av_log(NULL, AV_LOG_WARNING,
                   "No codec could be found with id %d\n", avctx->codec_id);
            ret = AVERROR(EINVAL);
            goto fail;
        }
    
        avctx->codec_id = codec->id;
        if(stream_lowres > av_codec_get_max_lowres(codec)){
            av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",
                   av_codec_get_max_lowres(codec));
            stream_lowres = av_codec_get_max_lowres(codec);
        }
        av_codec_set_lowres(avctx, stream_lowres);
    
    #if FF_API_EMU_EDGE
        if(stream_lowres) avctx->flags |= CODEC_FLAG_EMU_EDGE;
    #endif
        if (fast) {
            avctx->flags2 |= AV_CODEC_FLAG2_FAST;
        }
    #if FF_API_EMU_EDGE
        if(codec->capabilities & AV_CODEC_CAP_DR1) {
            avctx->flags |= CODEC_FLAG_EMU_EDGE;
        }
    #endif
    
        opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec);
        if (!av_dict_get(opts, "threads", NULL, 0)) {
            av_dict_set(&opts, "threads", "auto", 0);
        }
    
        if (stream_lowres) {
            av_dict_set_int(&opts, "lowres", stream_lowres, 0);
        }
    
        if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
            av_dict_set(&opts, "refcounted_frames", "1", 0);
        }
    
        if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {
            goto fail;
        }
    
        if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
            av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
            ret =  AVERROR_OPTION_NOT_FOUND;
            goto fail;
        }
    
        is->eof = 0;
        ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
        switch (avctx->codec_type) {
            case AVMEDIA_TYPE_AUDIO:
                // 计算采样率、声道等
                sample_rate    = avctx->sample_rate;
                nb_channels    = avctx->channels;
                channel_layout = avctx->channel_layout;
                /* prepare audio output */
                if ((ret = audio_open(is, channel_layout, nb_channels, sample_rate, &is->audio_tgt)) < 0) {
                    goto fail;
                }
                is->audio_hw_buf_size = ret;
                is->audio_src = is->audio_tgt;
                is->audio_buf_size  = 0;
                is->audio_buf_index = 0;
    
                /* init averaging filter */
                is->audio_diff_avg_coef  = exp(log(0.01) / AUDIO_DIFF_AVG_NB);
                is->audio_diff_avg_count = 0;
                /* since we do not have a precise anough audio FIFO fullness,
                   we correct audio sync only if larger than this threshold */
                is->audio_diff_threshold = (double)(is->audio_hw_buf_size) / is->audio_tgt.bytes_per_sec;
    
                is->audioStreamIdx = stream_index;
                is->audioStream = ic->streams[stream_index];
    
                // 初始化解码器
                decoder_init(&is->audioDecoder, avctx, &is->audioQueue, is->readCondition);
                if ((is->ic->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK))
                    && !is->ic->iformat->read_seek) {
                    is->audioDecoder.start_pts = is->audioStream->start_time;
                    is->audioDecoder.start_pts_tb = is->audioStream->time_base;
                }
                // 开启解码线程
                if ((ret = decoder_start(&is->audioDecoder, audio_thread, this)) < 0) {
                    goto out;
                }
                SDL_PauseAudio(0);
                break;
    
            case AVMEDIA_TYPE_VIDEO:
                is->videoStreamIdx = stream_index;
                is->videoStream = ic->streams[stream_index];
    
                decoder_init(&is->videoDecoder, avctx, &is->videoQueue, is->readCondition);
                if ((ret = decoder_start(&is->videoDecoder, video_thread, this)) < 0) {
                    goto out;
                }
                is->queue_attachments_req = 1;
                break;
    
            default:
                break;
        }
        goto out;
    
        fail:
        avcodec_free_context(&avctx);
        out:
        av_dict_free(&opts);
    
        return ret;
    }
    
    /**
     * 打开媒体流
     * @param filename
     * @param iformat
     * @return
     */
    VideoState *CainPlayer::stream_open(const char *filename) {
        VideoState *is;
    
        is = (VideoState *)av_mallocz(sizeof(VideoState));
        if (!is) {
            return NULL;
        }
        is->filename = av_strdup(filename);
        if (!is->filename) {
            goto fail;
        }
        is->ytop    = 0;
        is->xleft   = 0;
    
        // 初始化帧队列
        if (frame_queue_init(&is->videoFrameQueue, &is->videoQueue, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0) {
            goto fail;
        }
        if (frame_queue_init(&is->audioFrameQueue, &is->audioQueue, SAMPLE_QUEUE_SIZE, 1) < 0) {
            goto fail;
        }
        // 初始化裸数据包队列
        if (packet_queue_init(&is->videoQueue) < 0 ||
            packet_queue_init(&is->audioQueue) < 0) {
            goto fail;
        }
        // 创建读文件条件锁
        if (!(is->readCondition = CondCreate())) {
            av_log(NULL, AV_LOG_FATAL, "CondCreate(): %s\n", SDL_GetError());
            goto fail;
        }
        // 初始化时钟
        init_clock(&is->videoClock, &is->videoQueue.serial);
        init_clock(&is->audioClock, &is->audioQueue.serial);
        init_clock(&is->extClock, &is->extClock.serial);
        is->audio_clock_serial = -1;
    
        // 计算起始音量大小
        if (startup_volume < 0) {
            av_log(NULL, AV_LOG_WARNING, "-volume=%d < 0, setting to 0\n", startup_volume);
        }
        if (startup_volume > 100) {
            av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", startup_volume);
        }
        startup_volume = av_clip(startup_volume, 0, 100);
        startup_volume = av_clip(SDL_MIX_MAXVOLUME * startup_volume / 100, 0, SDL_MIX_MAXVOLUME);
        is->audio_volume = startup_volume;
        is->muted = 0;
        is->av_sync_type = av_sync_type;
    
        is->videRefreshThread = ThreadCreate(video_refresh_thread, this, "refreshThread");
        if (!is->videRefreshThread) {
            av_log(NULL, AV_LOG_FATAL, "ThreadCreate(): %s\n", SDL_GetError());
            goto fail;
        }
    
        // 创建读文件线程
        is->readThread     = ThreadCreate(read_thread,  this, "read_thread");
        if (!is->readThread) {
            av_log(NULL, AV_LOG_FATAL, "ThreadCreate(): %s\n", SDL_GetError());
            fail:
            stream_close(is);
            return NULL;
        }
        return is;
    }
    
    /**
     * 关闭媒体流
     * @param is
     * @param stream_index
     */
    void CainPlayer::stream_component_close(VideoState *is, int stream_index) {
        AVFormatContext *ic = is->ic;
        AVCodecParameters *codecpar;
    
        if (stream_index < 0 || stream_index >= ic->nb_streams)
            return;
        codecpar = ic->streams[stream_index]->codecpar;
    
        switch (codecpar->codec_type) {
            case AVMEDIA_TYPE_AUDIO:
                decoder_abort(&is->audioDecoder, &is->audioFrameQueue);
                SDL_CloseAudio();
                decoder_destroy(&is->audioDecoder);
                swr_free(&is->swr_ctx);
                av_freep(&is->audio_buf1);
                is->audio_buf1_size = 0;
                is->audio_buf = NULL;
                break;
    
            case AVMEDIA_TYPE_VIDEO:
                decoder_abort(&is->videoDecoder, &is->videoFrameQueue);
                decoder_destroy(&is->videoDecoder);
                break;
    
            default:
                break;
        }
    
        ic->streams[stream_index]->discard = AVDISCARD_ALL;
        switch (codecpar->codec_type) {
            case AVMEDIA_TYPE_AUDIO:
                is->audioStream = NULL;
                is->audioStreamIdx = -1;
                break;
    
            case AVMEDIA_TYPE_VIDEO:
                is->videoStream = NULL;
                is->videoStreamIdx = -1;
                break;
    
            default:
                break;
        }
    }
    
    /**
     * 关闭媒体流
     * @param is
     */
    void CainPlayer::stream_close(VideoState *is) {
        // 等待读文件线程退出
        is->abort_request = 1;
        ThreadWait(is->readThread, NULL);
    
        /* close each stream */
        if (is->audioStreamIdx >= 0) {
            stream_component_close(is, is->audioStreamIdx);
        }
        if (is->videoStreamIdx >= 0) {
            stream_component_close(is, is->videoStreamIdx);
        }
        // 关闭输入上下文
        avformat_close_input(&is->ic);
    
        // 等待刷新画面线程退出
        ThreadWait(is->videRefreshThread, NULL);
    
        // 销毁裸数据包队列
        packet_queue_destroy(&is->videoQueue);
        packet_queue_destroy(&is->audioQueue);
    
        // 销毁帧队列
        frame_queue_destory(&is->videoFrameQueue);
        frame_queue_destory(&is->audioFrameQueue);
    
        // 销毁读文件条件锁
        CondDestroy(is->readCondition);
        // 销毁转码上下文
        sws_freeContext(is->img_convert_ctx);
        av_free(is->filename);
        if (is->vid_texture) {
            SDL_DestroyTexture(is->vid_texture);
        }
        av_free(is);
    }
    
    /**
     * 退出播放器
     */
    void CainPlayer::exitPlayer() {
        do_exit(is);
    }
    
    /**
     * 退出播放器
     * @param is
     */
    void CainPlayer::do_exit(VideoState *is) {
        if (is) {
            stream_close(is);
        }
        if (renderer) {
            SDL_DestroyRenderer(renderer);
        }
        if (window) {
            SDL_DestroyWindow(window);
        }
        av_lockmgr_register(NULL);
    
        av_dict_free(&swr_opts);
        av_dict_free(&sws_dict);
        av_dict_free(&format_opts);
        av_dict_free(&codec_opts);
        av_dict_free(&resample_opts);
    
        avformat_network_deinit();
        SDL_Quit();
        av_log(NULL, AV_LOG_QUIET, "%s", "");
        exit(0);
    }
    
    void CainPlayer::sigterm_handler(int sig) {
        exit(123);
    }
    
    /**
     * 设置大小
     * @param width
     * @param height
     * @param sar
     */
    void CainPlayer::set_default_window_size(int width, int height, AVRational sar) {
        SDL_Rect rect;
        calculate_display_rect(&rect, 0, 0, INT_MAX, height, width, height, sar);
        default_width  = rect.w;
        default_height = rect.h;
    }
    
    /**
     * 打开视频,主要用于创建视频帧的渲染器
     * @param is
     * @return
     */
    int CainPlayer::video_open(VideoState *is) {
        int w,h;
    
        if (screen_width) {
            w = screen_width;
            h = screen_height;
        } else {
            w = default_width;
            h = default_height;
        }
    
        if (!window) {
            int flags = SDL_WINDOW_SHOWN;
            flags |= SDL_WINDOW_RESIZABLE;
            window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, flags);
            SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
            if (window) {
                SDL_RendererInfo info;
                renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
                if (!renderer) {
                    av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());
                    renderer = SDL_CreateRenderer(window, -1, 0);
                }
                if (renderer) {
                    if (!SDL_GetRendererInfo(renderer, &info))
                        av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", info.name);
                }
            }
        } else {
            SDL_SetWindowSize(window, w, h);
        }
    
        if (!window || !renderer) {
            av_log(NULL, AV_LOG_FATAL, "SDL: could not set video mode - exiting\n");
            do_exit(is);
        }
    
        is->width  = w;
        is->height = h;
    
        return 0;
    }
    
    /**
     * 视频显示
     * @param is
     */
    void CainPlayer::video_display(VideoState *is) {
        if (!window) {
            video_open(is);
        }
        if (is->show_mode == SHOW_MODE_VIDEO) {
            SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
            SDL_RenderClear(renderer);
            if (is->videoStream) {
                video_image_display(is);
            }
            SDL_RenderPresent(renderer);
        }
    }
    
    /**
     * 获取主同步类型
     * @param is
     * @return
     */
    int CainPlayer::get_master_sync_type(VideoState *is) {
        if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
            if (is->videoStream) {
                return AV_SYNC_VIDEO_MASTER;
            } else {
                return AV_SYNC_AUDIO_MASTER;
            }
        } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
            if (is->audioStream) {
                return AV_SYNC_AUDIO_MASTER;
            } else {
                return AV_SYNC_EXTERNAL_CLOCK;
            }
        } else {
            return AV_SYNC_EXTERNAL_CLOCK;
        }
    }
    
    /**
     * 获取主时钟
     * @param is
     * @return
     */
    double CainPlayer::get_master_clock(VideoState *is) {
        double val;
    
        switch (get_master_sync_type(is)) {
            case AV_SYNC_VIDEO_MASTER:
                val = get_clock(&is->videoClock);
                break;
            case AV_SYNC_AUDIO_MASTER:
                val = get_clock(&is->audioClock);
                break;
            default:
                val = get_clock(&is->extClock);
                break;
        }
        return val;
    }
    
    /**
     * 检查外部时钟速度
     * @param is
     */
    void CainPlayer::check_external_clock_speed(VideoState *is) {
        if (is->videoStreamIdx >= 0 && is->videoQueue.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES ||
            is->audioStreamIdx >= 0 && is->audioQueue.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES) {
    
            set_clock_speed(&is->extClock, FFMAX(EXTERNAL_CLOCK_SPEED_MIN,
                                                 is->extClock.speed - EXTERNAL_CLOCK_SPEED_STEP));
    
        } else if ((is->videoStreamIdx < 0 || is->videoQueue.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES) &&
                   (is->audioStreamIdx < 0 || is->audioQueue.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES)) {
    
            set_clock_speed(&is->extClock, FFMIN(EXTERNAL_CLOCK_SPEED_MAX,
                                                 is->extClock.speed + EXTERNAL_CLOCK_SPEED_STEP));
        } else {
            double speed = is->extClock.speed;
            if (speed != 1.0) {
                set_clock_speed(&is->extClock,
                                speed + EXTERNAL_CLOCK_SPEED_STEP * (1.0 - speed) / fabs(1.0 - speed));
            }
        }
    }
    
    /**
     * 定位
     * @param is
     * @param pos
     * @param rel
     * @param seek_by_bytes
     */
    void CainPlayer::stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes) {
        if (!is->seek_req) {
            is->seek_pos = pos;
            is->seek_rel = rel;
            is->seek_flags &= ~AVSEEK_FLAG_BYTE;
            if (seek_by_bytes) {
                is->seek_flags |= AVSEEK_FLAG_BYTE;
            }
            is->seek_req = 1;
            CondSignal(is->readCondition);
        }
    }
    
    /**
     * 暂停/播放
     * @param is
     */
    void CainPlayer::stream_toggle_pause(VideoState *is) {
        if (is->paused) {
            is->frame_timer += av_gettime_relative() / 1000000.0 - is->videoClock.last_updated;
            if (is->read_pause_return != AVERROR(ENOSYS)) {
                is->videoClock.paused = 0;
            }
            set_clock(&is->videoClock, get_clock(&is->videoClock), is->videoClock.serial);
        }
        set_clock(&is->extClock, get_clock(&is->extClock), is->extClock.serial);
        is->paused = is->audioClock.paused = is->videoClock.paused = is->extClock.paused = !is->paused;
    }
    
    /**
     * 暂停/播放
     * @param is
     */
    void CainPlayer::toggle_pause(VideoState *is) {
        stream_toggle_pause(is);
        is->step = 0;
    }
    
    /**
     * 静音/播放声音
     * @param is
     */
    void CainPlayer::toggle_mute(VideoState *is) {
        is->muted = !is->muted;
    }
    
    /**
     * 更新音频
     * @param is
     * @param sign > 0 为增加的音量,< 0 为减少音量
     * @param step
     */
    void CainPlayer::update_volume(VideoState *is, int sign, double step) {
        double volume_level = is->audio_volume ? (20 * log(is->audio_volume / (double)SDL_MIX_MAXVOLUME) / log(10)) : -1000.0;
        int new_volume = lrint(SDL_MIX_MAXVOLUME * pow(10.0, (volume_level + sign * step) / 20.0));
        is->audio_volume = av_clip(is->audio_volume == new_volume ? (is->audio_volume + sign) : new_volume, 0, SDL_MIX_MAXVOLUME);
    }
    
    /**
     * 跳到下一帧
     * @param is
     */
    void CainPlayer::step_to_next_frame(VideoState *is) {
        /* if the stream is paused unpause it, then step */
        if (is->paused) {
            stream_toggle_pause(is);
        }
        is->step = 1;
    }
    
    /**
     * 计算延时
     * @param delay
     * @param is
     * @return
     */
    double CainPlayer::compute_target_delay(double delay, VideoState *is) {
        double sync_threshold, diff = 0;
    
        /* update delay to follow master synchronisation source */
        if (get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER) {
            /* if video is slave, we try to correct big delays by
               duplicating or deleting a frame */
            diff = get_clock(&is->videoClock) - get_master_clock(is);
    
            /* skip or repeat frame. We take into account the
               delay to compute the threshold. I still don't know
               if it is the best guess */
            sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
            if (!isnan(diff) && fabs(diff) < is->max_frame_duration) {
                if (diff <= -sync_threshold)
                    delay = FFMAX(0, delay + diff);
                else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD)
                    delay = delay + diff;
                else if (diff >= sync_threshold)
                    delay = 2 * delay;
            }
        }
    
        av_log(NULL, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n",
               delay, -diff);
    
        return delay;
    }
    
    /**
     * 计算显示时长
     * @param is
     * @param vp
     * @param nextvp
     * @return
     */
    double CainPlayer::vp_duration(VideoState *is, Frame *vp, Frame *nextvp) {
        if (vp->serial == nextvp->serial) {
            double duration = nextvp->pts - vp->pts;
            if (isnan(duration) || duration <= 0 || duration > is->max_frame_duration)
                return vp->duration;
            else
                return duration;
        } else {
            return 0.0;
        }
    }
    
    /**
     * 更新pts
     * @param is
     * @param pts
     * @param pos
     * @param serial
     */
    void CainPlayer::update_video_pts(VideoState *is, double pts, int64_t pos, int serial) {
        /* update current video pts */
        set_clock(&is->videoClock, pts, serial);
        sync_clock_to_slave(&is->extClock, &is->videoClock);
    }
    
    /**
     * 刷新视频画面
     * @param opaque
     * @param remaining_time
     */
    void CainPlayer::video_refresh(void *opaque, double *remaining_time) {
        VideoState *is = (VideoState *)opaque;
        double time;
    
        // 实时流状态下同步到外部时钟时,需要检查外部时钟的速度
        if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime) {
            check_external_clock_speed(is);
        }
    
        if (is->videoStream) {
            retry:
            // 帧队列中存在数据时,则不断读出
            if (frame_queue_nb_remaining(&is->videoFrameQueue) > 0) {
                double last_duration, duration, delay;
                Frame *vp, *lastvp;
    
                /* dequeue the picture */
                // 上一帧
                lastvp = frame_queue_peek_last(&is->videoFrameQueue);
                // 当前帧
                vp = frame_queue_peek(&is->videoFrameQueue);
                // 如果当前帧序列跟裸数据包的序列不一致时,需要跳帧
                if (vp->serial != is->videoQueue.serial) {
                    frame_queue_next(&is->videoFrameQueue);
                    goto retry;
                }
                // 如果上一帧的序列跟当前帧的序列不一致,则需要重新计算帧计时器,因为有可能跳帧了
                if (lastvp->serial != vp->serial) {
                    is->frame_timer = av_gettime_relative() / 1000000.0;
                }
                // 如果处于暂停状态,则直接显示
                if (is->paused) {
                    goto display;
                }
    
                /* compute nominal last_duration */
                // 计算上一次显示时长
                last_duration = vp_duration(is, lastvp, vp);
                // 根据上一次显示的时长,计算延时
                delay = compute_target_delay(last_duration, is);
                // 获取当前时间
                time= av_gettime_relative() / 1000000.0;
                // 如果当前时间小于帧计时器的时间 + 延时时间,则表示还没到当前帧
                if (time < is->frame_timer + delay) {
                    *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
                    goto display;
                }
    
                // 更新帧计时器
                is->frame_timer += delay;
                // 帧计时器落后当前时间超过了阈值,则用当前的时间作为帧计时器时间
                if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX) {
                    is->frame_timer = time;
                }
                // 更新帧的pt和序列
                MutexLock(is->videoFrameQueue.mutex);
                if (!isnan(vp->pts)) {
                    update_video_pts(is, vp->pts, vp->pos, vp->serial);
                }
                MutexUnlock(is->videoFrameQueue.mutex);
                // 如果队列中还剩余超过一帧的数据时,需要拿到下一帧,然后计算间隔,并判断是否需要进行舍帧操作
                if (frame_queue_nb_remaining(&is->videoFrameQueue) > 1) {
                    Frame *nextvp = frame_queue_peek_next(&is->videoFrameQueue);
                    duration = vp_duration(is, vp, nextvp);
                    // 如果不处于同步到视频状态,并且处于跳帧状态,则跳过当前帧
                    if(!is->step && (framedrop > 0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER))
                       && time > is->frame_timer + duration){
                        frame_queue_next(&is->videoFrameQueue);
                        goto retry;
                    }
                }
    
                frame_queue_next(&is->videoFrameQueue);
                is->force_refresh = 1;
    
                if (is->step && !is->paused) {
                    stream_toggle_pause(is);
                }
            }
            display:
            /* display picture */
            if (!display_disable && is->force_refresh
                && is->show_mode == SHOW_MODE_VIDEO && is->videoFrameQueue.rindex_shown) {
                video_display(is);
            }
        }
        is->force_refresh = 0;
    }
    
    /**
     * 将解码后的帧入队
     * @param is
     * @param src_frame
     * @param pts
     * @param duration
     * @param pos
     * @param serial
     * @return
     */
    int CainPlayer::queue_picture(VideoState *is, AVFrame *src_frame, double pts, double duration, int64_t pos, int serial) {
        Frame *vp;
    
        if (!(vp = frame_queue_peek_writable(&is->videoFrameQueue))) {
            return -1;
        }
    
        vp->sar = src_frame->sample_aspect_ratio;
        vp->uploaded = 0;
    
        vp->width = src_frame->width;
        vp->height = src_frame->height;
        vp->format = src_frame->format;
    
        vp->pts = pts;
        vp->duration = duration;
        vp->pos = pos;
        vp->serial = serial;
    
        set_default_window_size(vp->width, vp->height, vp->sar);
    
        av_frame_move_ref(vp->frame, src_frame);
        frame_queue_push(&is->videoFrameQueue);
        return 0;
    }
    
    /**
     * 获得解码后的视频帧
     * @param is
     * @param frame
     * @return
     */
    int CainPlayer::get_video_frame(VideoState *is, AVFrame *frame) {
        int got_picture;
    
        if ((got_picture = decoder_decode_frame(&is->videoDecoder, frame, NULL)) < 0) {
            return -1;
        }
    
        if (got_picture) {
            double dpts = NAN;
    
            if (frame->pts != AV_NOPTS_VALUE) {
                dpts = av_q2d(is->videoStream->time_base) * frame->pts;
            }
            // 计算视频帧的长宽比
            frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->videoStream, frame);
            // 是否需要做舍帧操作
            if (framedrop > 0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) {
                if (frame->pts != AV_NOPTS_VALUE) {
                    double diff = dpts - get_master_clock(is);
                    if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD &&
                        diff - is->frame_last_filter_delay < 0 &&
                        is->videoDecoder.pkt_serial == is->videoClock.serial &&
                        is->videoQueue.nb_packets) {
                        av_frame_unref(frame);
                        got_picture = 0;
                    }
                }
            }
        }
    
        return got_picture;
    }
    
    int CainPlayer::read_thread(void *arg) {
        CainPlayer * player = (CainPlayer *) arg;
        return player->demux();
    }
    
    int CainPlayer::audio_thread(void *arg) {
        CainPlayer * player = (CainPlayer *) arg;
        return player->audioDecode();
    }
    
    int CainPlayer::video_thread(void *arg) {
        CainPlayer * player = (CainPlayer *) arg;
        return player->videoDecode();
    }
    
    
    
    /**
     * 解复用
     * @return
     */
    int CainPlayer::demux() {
        AVFormatContext *ic = NULL;
        int err, i, ret;
        int st_index[AVMEDIA_TYPE_NB];
        AVPacket pkt1, *pkt = &pkt1;
        int64_t stream_start_time;
        int pkt_in_play_range = 0;
        AVDictionaryEntry *t;
        AVDictionary **opts;
        int orig_nb_streams;
        Mutex *wait_mutex = MutexCreate();
        int scan_all_pmts_set = 0;
        int64_t pkt_ts;
    
        // 创建等待互斥锁
        if (!wait_mutex) {
            av_log(NULL, AV_LOG_FATAL, "MutexCreate(): %s\n", SDL_GetError());
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    
        memset(st_index, -1, sizeof(st_index));
        is->videoStreamIdx = -1;
        is->audioStreamIdx = -1;
        is->eof = 0;
    
        // 创建解复用上下文
        ic = avformat_alloc_context();
        if (!ic) {
            av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    
        // 设置解复用中断回调
        ic->interrupt_callback.callback = decode_interrupt_cb;
        ic->interrupt_callback.opaque = is;
        if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
            av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
            scan_all_pmts_set = 1;
        }
    
        // 打开视频文件
        err = avformat_open_input(&ic, is->filename, NULL, &format_opts);
        if (err < 0) {
            print_error(is->filename, err);
            ret = -1;
            goto fail;
        }
    
        if (scan_all_pmts_set) {
            av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
        }
    
        if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
            av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
            ret = AVERROR_OPTION_NOT_FOUND;
            goto fail;
        }
        is->ic = ic;
    
        if (genpts) {
            ic->flags |= AVFMT_FLAG_GENPTS;
        }
        av_format_inject_global_side_data(ic);
    
        opts = setup_find_stream_info_opts(ic, codec_opts);
        orig_nb_streams = ic->nb_streams;
        // 查找媒体流信息
        err = avformat_find_stream_info(ic, opts);
        for (i = 0; i < orig_nb_streams; i++) {
            av_dict_free(&opts[i]);
        }
        av_freep(&opts);
    
        if (err < 0) {
            av_log(NULL, AV_LOG_WARNING,
                   "%s: could not find codec parameters\n", is->filename);
            ret = -1;
            goto fail;
        }
    
        if (ic->pb) {
            ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use avio_feof() to test for the end
        }
    
        // 判断是否以字节方式定位
        if (seek_by_bytes < 0) {
            seek_by_bytes =
                    !!(ic->iformat->flags & AVFMT_TS_DISCONT) && strcmp("ogg", ic->iformat->name);
        }
        // 计算帧的最大显示时长
        is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0;
    
        // 如果不是从头开始播放,则跳转播放位置
        if (start_time != AV_NOPTS_VALUE) {
            int64_t timestamp;
    
            timestamp = start_time;
            /* add the stream start time */
            if (ic->start_time != AV_NOPTS_VALUE)
                timestamp += ic->start_time;
            ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);
            if (ret < 0) {
                av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",
                       is->filename, (double)timestamp / AV_TIME_BASE);
            }
        }
    
        // 判断是否属于实时流
        is->realtime = is_realtime(ic);
        // 查找媒体流
        for (i = 0; i < ic->nb_streams; i++) {
            AVStream *st = ic->streams[i];
            enum AVMediaType type = st->codecpar->codec_type;
            st->discard = AVDISCARD_ALL;
            if (type >= 0 && wanted_stream_spec[type] && st_index[type] == -1) {
                if (avformat_match_stream_specifier(ic, st, wanted_stream_spec[type]) > 0) {
                    st_index[type] = i;
                }
            }
        }
    
        for (i = 0; i < AVMEDIA_TYPE_NB; i++) {
            if (wanted_stream_spec[i] && st_index[i] == -1) {
                av_log(NULL, AV_LOG_ERROR, "Stream specifier %s does not match any %s stream\n",
                       wanted_stream_spec[i], av_get_media_type_string((AVMediaType)i));
                st_index[i] = INT_MAX;
            }
        }
        // 是否禁止视频流
        if (!video_disable) {
            st_index[AVMEDIA_TYPE_VIDEO] =
                    av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
                                        st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
        }
        // 是否禁止音频流
        if (!audio_disable) {
            st_index[AVMEDIA_TYPE_AUDIO] =
                    av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,
                                        st_index[AVMEDIA_TYPE_AUDIO],
                                        st_index[AVMEDIA_TYPE_VIDEO],
                                        NULL, 0);
        }
        // 设置显示模式
        is->show_mode = show_mode;
        // 判断视频流索引是否存在,用来计算视频的显示区域
        if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
            AVStream *st = ic->streams[st_index[AVMEDIA_TYPE_VIDEO]];
            AVCodecParameters *codecpar = st->codecpar;
            // 计算采样长宽比
            AVRational sar = av_guess_sample_aspect_ratio(ic, st, NULL);
            // 设置默认的窗口大小
            if (codecpar->width) {
                set_default_window_size(codecpar->width, codecpar->height, sar);
            }
        }
    
        // 打开音频流
        if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
            stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
        }
    
        // 打开视频流
        ret = -1;
        if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
            ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
        }
    
        // 是否需要重新设置显示模式
        if (is->show_mode == SHOW_MODE_NONE) {
            is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT;
        }
    
        // 判断音频流或者视频流是否打开了
        if (is->videoStreamIdx < 0 && is->audioStreamIdx < 0) {
            av_log(NULL, AV_LOG_FATAL, "Failed to open file '%s' or configure filtergraph\n",
                   is->filename);
            ret = -1;
            goto fail;
        }
        // 如果是实时流,设置无线缓冲区
        if (infinite_buffer < 0 && is->realtime) {
            infinite_buffer = 1;
        }
    
        // 进入解复用阶段
        for (;;) {
            // 停止播放
            if (is->abort_request) {
                break;
            }
            // 暂停状态
            if (is->paused != is->last_paused) {
                is->last_paused = is->paused;
                if (is->paused) { // 如果此时处于暂停状态,则停止读文件
                    is->read_pause_return = av_read_pause(ic);
                } else {
                    av_read_play(ic);
                }
            }
    #if CONFIG_RTSP_DEMUXER || CONFIG_MMSH_PROTOCOL
            // 如果此时处于暂停状态,并且不是rtsp、mmsh实时流,则延时10毫秒在继续下一轮读操作
            if (is->paused &&
                    (!strcmp(ic->iformat->name, "rtsp") ||
                     (ic->pb && !strncmp(input_filename, "mmsh:", 5)))) {
                /* wait 10 ms to avoid trying to get another packet */
                /* XXX: horrible */
                SDL_Delay(10);
                continue;
            }
    #endif
            // 如果处于定位操作状态,则进入定位操作
            if (is->seek_req) {
                int64_t seek_target = is->seek_pos;
                int64_t seek_min    = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;
                int64_t seek_max    = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX;
                // 定位
                ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
                if (ret < 0) {
                    av_log(NULL, AV_LOG_ERROR,
                           "%s: error while seeking\n", is->ic->filename);
                } else {
                    if (is->audioStreamIdx >= 0) {
                        packet_queue_flush(&is->audioQueue);
                        packet_queue_put(&is->audioQueue, &flush_pkt);
                    }
                    if (is->videoStreamIdx >= 0) {
                        packet_queue_flush(&is->videoQueue);
                        packet_queue_put(&is->videoQueue, &flush_pkt);
                    }
                    if (is->seek_flags & AVSEEK_FLAG_BYTE) {
                        set_clock(&is->extClock, NAN, 0);
                    } else {
                        set_clock(&is->extClock, seek_target / (double)AV_TIME_BASE, 0);
                    }
                }
                is->seek_req = 0;
                is->queue_attachments_req = 1;
                is->eof = 0;
                // 如果此时处于暂停状态,则调到下一帧
                if (is->paused) {
                    step_to_next_frame(is);
                }
            }
            // 附着请求状态
            if (is->queue_attachments_req) {
                // 判断视频流是否存在
                if (is->videoStream && is->videoStream->disposition & AV_DISPOSITION_ATTACHED_PIC) {
                    AVPacket copy;
                    if ((ret = av_copy_packet(&copy, &is->videoStream->attached_pic)) < 0) {
                        goto fail;
                    }
                    packet_queue_put(&is->videoQueue, &copy);
                    packet_queue_put_nullpacket(&is->videoQueue, is->videoStreamIdx);
                }
                is->queue_attachments_req = 0;
            }
    
            // 如果队列已满,不需要再继续读了
            if (infinite_buffer < 1 &&
                (is->audioQueue.size + is->videoQueue.size > MAX_QUEUE_SIZE
                 || (stream_has_enough_packets(is->audioStream, is->audioStreamIdx, &is->audioQueue) &&
                     stream_has_enough_packets(is->videoStream, is->videoStreamIdx, &is->videoQueue)))) {
                /* wait 10 ms */
                MutexLock(wait_mutex);
                CondWaitTimeout(is->readCondition, wait_mutex, 10);
                MutexUnlock(wait_mutex);
                continue;
            }
            // 如果此时不能处于暂停状态,并且为了播放到结尾了,判断是否需要循环播放。
            if (!is->paused &&
                (!is->audioStream || (is->audioDecoder.finished == is->audioQueue.serial && frame_queue_nb_remaining(&is->audioFrameQueue) == 0)) &&
                (!is->videoStream || (is->videoDecoder.finished == is->videoQueue.serial && frame_queue_nb_remaining(&is->videoFrameQueue) == 0))) {
                if (loop != 1 && (!loop || --loop)) {
                    stream_seek(is, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0);
                } else if (autoexit) {
                    ret = AVERROR_EOF;
                    goto fail;
                }
            }
            // 读出裸数据包
            ret = av_read_frame(ic, pkt);
            if (ret < 0) {
                // 如果没能读出裸数据包,判断是否是结尾
                if ((ret == AVERROR_EOF || avio_feof(ic->pb)) && !is->eof) {
                    if (is->videoStreamIdx >= 0) {
                        packet_queue_put_nullpacket(&is->videoQueue, is->videoStreamIdx);
                    }
                    if (is->audioStreamIdx >= 0) {
                        packet_queue_put_nullpacket(&is->audioQueue, is->audioStreamIdx);
                    }
                    is->eof = 1;
                }
                if (ic->pb && ic->pb->error) {
                    break;
                }
                MutexLock(wait_mutex);
                CondWaitTimeout(is->readCondition, wait_mutex, 10);
                MutexUnlock(wait_mutex);
                continue;
            } else {
                is->eof = 0;
            }
            // 计算pkt的pts是否处于播放范围内
            stream_start_time = ic->streams[pkt->stream_index]->start_time;
            pkt_ts = pkt->pts == AV_NOPTS_VALUE ? pkt->dts : pkt->pts;
            // 播放范围内
            pkt_in_play_range = duration == AV_NOPTS_VALUE ||
                                (pkt_ts - (stream_start_time != AV_NOPTS_VALUE ? stream_start_time : 0)) *
                                av_q2d(ic->streams[pkt->stream_index]->time_base) -
                                (double)(start_time != AV_NOPTS_VALUE ? start_time : 0) / 1000000
                                <= ((double)duration / 1000000);
            if (pkt->stream_index == is->audioStreamIdx && pkt_in_play_range) {
                packet_queue_put(&is->audioQueue, pkt);
            } else if (pkt->stream_index == is->videoStreamIdx && pkt_in_play_range
                       && !(is->videoStream->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
                packet_queue_put(&is->videoQueue, pkt);
            } else {
                av_packet_unref(pkt);
            }
        }
    
        ret = 0;
        fail:
        if (ic && !is->ic) {
            avformat_close_input(&ic);
        }
        // 如果结果不为0,则发送退出指令
        if (ret != 0) {
            SDL_Event event;
    
            event.type = FF_QUIT_EVENT;
            event.user.data1 = is;
            SDL_PushEvent(&event);
        }
        MutexDestroy(wait_mutex);
        return 0;
    }
    
    
    /**
     * 音频解码
     * @return
     */
    int CainPlayer::audioDecode() {
        AVFrame *frame = av_frame_alloc();
        Frame *af;
        int got_frame = 0;
        AVRational tb;
        int ret = 0;
    
        if (!frame) {
            return AVERROR(ENOMEM);
        }
    
        do {
            if ((got_frame = decoder_decode_frame(&is->audioDecoder, frame, NULL)) < 0) {
                goto the_end;
            }
    
            if (got_frame) {
                tb = (AVRational){1, frame->sample_rate};
                if (!(af = frame_queue_peek_writable(&is->audioFrameQueue))){
                    goto the_end;
                }
    
                af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
                af->pos = av_frame_get_pkt_pos(frame);
                af->serial = is->audioDecoder.pkt_serial;
                af->duration = av_q2d((AVRational){frame->nb_samples, frame->sample_rate});
    
                av_frame_move_ref(af->frame, frame);
                frame_queue_push(&is->audioFrameQueue);
            }
        } while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF);
    the_end:
        av_frame_free(&frame);
        return ret;
    }
    
    /**
     * 视频解码
     * @return
     */
    int CainPlayer::videoDecode() {
        AVFrame *frame = av_frame_alloc();
        double pts;
        double duration;
        int ret;
        AVRational tb = is->videoStream->time_base;
        AVRational frame_rate = av_guess_frame_rate(is->ic, is->videoStream, NULL);
    
        if (!frame) {
            return AVERROR(ENOMEM);
        }
    
        for (;;) {
            ret = get_video_frame(is, frame);
            if (ret < 0)
                goto the_end;
            if (!ret) {
                continue;
            }
            duration = (frame_rate.num && frame_rate.den
                        ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0);
            pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
            ret = queue_picture(is, frame, pts, duration,
                                av_frame_get_pkt_pos(frame), is->videoDecoder.pkt_serial);
            av_frame_unref(frame);
            if (ret < 0) {
                goto the_end;
            }
        }
    the_end:
        av_frame_free(&frame);
        return 0;
    }
    
    /**
     * 同步音频
     * @param is
     * @param nb_samples
     * @return
     */
    int CainPlayer::synchronize_audio(VideoState *is, int nb_samples) {
        int wanted_nb_samples = nb_samples;
    
        /* if not master, then we try to remove or add samples to correct the clock */
        if (get_master_sync_type(is) != AV_SYNC_AUDIO_MASTER) {
            double diff, avg_diff;
            int min_nb_samples, max_nb_samples;
    
            diff = get_clock(&is->audioClock) - get_master_clock(is);
    
            if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD) {
                is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum;
                if (is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) {
                    /* not enough measures to have a correct estimate */
                    is->audio_diff_avg_count++;
                } else {
                    /* estimate the A-V difference */
                    avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef);
    
                    if (fabs(avg_diff) >= is->audio_diff_threshold) {
                        wanted_nb_samples = nb_samples + (int)(diff * is->audio_src.freq);
                        min_nb_samples = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100));
                        max_nb_samples = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100));
                        wanted_nb_samples = av_clip(wanted_nb_samples, min_nb_samples, max_nb_samples);
                    }
                }
            } else {
                /* too big difference : may be initial PTS errors, so
                   reset A-V filter */
                is->audio_diff_avg_count = 0;
                is->audio_diff_cum       = 0;
            }
        }
    
        return wanted_nb_samples;
    }
    
    /**
     * 音频解码
     * @param is
     * @return
     */
    int CainPlayer::audio_decode_frame(VideoState *is) {
        int data_size, resampled_data_size;
        int64_t dec_channel_layout;
        av_unused double audio_clock0;
        int wanted_nb_samples;
        Frame *af;
    
        // 处于暂停状态
        if (is->paused) {
            return -1;
        }
    
        do {
            if (!(af = frame_queue_peek_readable(&is->audioFrameQueue))) {
                return -1;
            }
            frame_queue_next(&is->audioFrameQueue);
        } while (af->serial != is->audioQueue.serial);
    
        data_size = av_samples_get_buffer_size(NULL, av_frame_get_channels(af->frame),
                                               af->frame->nb_samples,
                                               (AVSampleFormat)af->frame->format, 1);
    
        dec_channel_layout =
                (af->frame->channel_layout && av_frame_get_channels(af->frame) == av_get_channel_layout_nb_channels(af->frame->channel_layout))
                ? af->frame->channel_layout : av_get_default_channel_layout(av_frame_get_channels(af->frame));
        wanted_nb_samples = synchronize_audio(is, af->frame->nb_samples);
    
        if (af->frame->format        != is->audio_src.fmt            ||
            dec_channel_layout       != is->audio_src.channel_layout ||
            af->frame->sample_rate   != is->audio_src.freq           ||
            (wanted_nb_samples       != af->frame->nb_samples && !is->swr_ctx)) {
            swr_free(&is->swr_ctx);
            is->swr_ctx = swr_alloc_set_opts(NULL, is->audio_tgt.channel_layout, is->audio_tgt.fmt,
                                             is->audio_tgt.freq, dec_channel_layout,
                                             (AVSampleFormat)af->frame->format, af->frame->sample_rate,
                                             0, NULL);
    
            if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
                av_log(NULL, AV_LOG_ERROR,
                       "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
                       af->frame->sample_rate, av_get_sample_fmt_name((AVSampleFormat)af->frame->format), av_frame_get_channels(af->frame),
                       is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels);
                swr_free(&is->swr_ctx);
                return -1;
            }
            is->audio_src.channel_layout = dec_channel_layout;
            is->audio_src.channels       = av_frame_get_channels(af->frame);
            is->audio_src.freq = af->frame->sample_rate;
            is->audio_src.fmt = (AVSampleFormat)af->frame->format;
        }
    
        if (is->swr_ctx) {
            const uint8_t **in = (const uint8_t **)af->frame->extended_data;
            uint8_t **out = &is->audio_buf1;
            int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate + 256;
            int out_size  = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, out_count, is->audio_tgt.fmt, 0);
            int len2;
            if (out_size < 0) {
                av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n");
                return -1;
            }
            if (wanted_nb_samples != af->frame->nb_samples) {
                if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - af->frame->nb_samples) * is->audio_tgt.freq / af->frame->sample_rate,
                                         wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate) < 0) {
                    av_log(NULL, AV_LOG_ERROR, "swr_set_compensation() failed\n");
                    return -1;
                }
            }
            av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size);
            if (!is->audio_buf1)
                return AVERROR(ENOMEM);
            len2 = swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples);
            if (len2 < 0) {
                av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n");
                return -1;
            }
            if (len2 == out_count) {
                av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n");
                if (swr_init(is->swr_ctx) < 0)
                    swr_free(&is->swr_ctx);
            }
            is->audio_buf = is->audio_buf1;
            resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
        } else {
            is->audio_buf = af->frame->data[0];
            resampled_data_size = data_size;
        }
    
        /* update the audio clock with the pts */
        if (!isnan(af->pts)) {
            is->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate;
        } else {
            is->audio_clock = NAN;
        }
        is->audio_clock_serial = af->serial;
        return resampled_data_size;
    }
    
    /**
     * 音频设备回调
     * @param opaque
     * @param stream
     * @param len
     */
    void CainPlayer::sdl_audio_callback(void *opaque, Uint8 *stream, int len) {
        CainPlayer *player = (CainPlayer *) opaque;
        player->audio_callback(stream, len);
    }
    
    /**
     * 音频设备回调
     * @param stream
     * @param len
     */
    void CainPlayer::audio_callback(Uint8 *stream, int len) {
        int audio_size, len1;
    
        audio_callback_time = av_gettime_relative();
    
        while (len > 0) {
            if (is->audio_buf_index >= is->audio_buf_size) {
                audio_size = audio_decode_frame(is);
                if (audio_size < 0) {
                    /* if error, just output silence */
                    is->audio_buf = NULL;
                    is->audio_buf_size = (unsigned int) (SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size
                                                         * is->audio_tgt.frame_size);
                } else {
                    is->audio_buf_size = audio_size;
                }
                is->audio_buf_index = 0;
            }
            len1 = is->audio_buf_size - is->audio_buf_index;
            if (len1 > len) {
                len1 = len;
            }
            if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME) {
                memcpy(stream, (uint8_t *) is->audio_buf + is->audio_buf_index, len1);
            } else {
                memset(stream, 0, len1);
                if (!is->muted && is->audio_buf) {
                    SDL_MixAudio(stream, (uint8_t *) is->audio_buf + is->audio_buf_index, len1,
                                 is->audio_volume);
                }
            }
            len -= len1;
            stream += len1;
            is->audio_buf_index += len1;
        }
        is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index;
        /* Let's assume the audio driver that is used by SDL has two periods. */
        if (!isnan(is->audio_clock)) {
            set_clock_at(&is->audioClock,
                         is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec,
                         is->audio_clock_serial,
                         audio_callback_time / 1000000.0);
            sync_clock_to_slave(&is->extClock, &is->audioClock);
        }
    }
    
    /**
     * 打开音频设备
     * @param opaque
     * @param wanted_channel_layout
     * @param wanted_nb_channels
     * @param wanted_sample_rate
     * @param audio_hw_params
     * @return
     */
    int CainPlayer::audio_open(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels,
                               int wanted_sample_rate, struct AudioParams *audio_hw_params) {
        SDL_AudioSpec wanted_spec, spec;
        const char *env;
        static const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6};
        static const int next_sample_rates[] = {0, 44100, 48000, 96000, 192000};
        int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;
    
        env = SDL_getenv("SDL_AUDIO_CHANNELS");
        if (env) {
            wanted_nb_channels = atoi(env);
            wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
        }
        if (!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
            wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
            wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
        }
        wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
        wanted_spec.channels = wanted_nb_channels;
        wanted_spec.freq = wanted_sample_rate;
        if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
            av_log(NULL, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
            return -1;
        }
    
        while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq) {
            next_sample_rate_idx--;
        }
    
        wanted_spec.format = AUDIO_S16SYS;
        wanted_spec.silence = 0;
        wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC));
        wanted_spec.callback = sdl_audio_callback;
        wanted_spec.userdata = this;
        while (SDL_OpenAudio(&wanted_spec, &spec) < 0) {
            av_log(NULL, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
                   wanted_spec.channels, wanted_spec.freq, SDL_GetError());
            wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
            if (!wanted_spec.channels) {
                wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
                wanted_spec.channels = wanted_nb_channels;
                if (!wanted_spec.freq) {
                    av_log(NULL, AV_LOG_ERROR,
                           "No more combinations to try, audio open failed\n");
                    return -1;
                }
            }
            wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
        }
        if (spec.format != AUDIO_S16SYS) {
            av_log(NULL, AV_LOG_ERROR,
                   "SDL advised audio format %d is not supported!\n", spec.format);
            return -1;
        }
        if (spec.channels != wanted_spec.channels) {
            wanted_channel_layout = av_get_default_channel_layout(spec.channels);
            if (!wanted_channel_layout) {
                av_log(NULL, AV_LOG_ERROR,
                       "SDL advised channel count %d is not supported!\n", spec.channels);
                return -1;
            }
        }
    
        audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
        audio_hw_params->freq = spec.freq;
        audio_hw_params->channel_layout = wanted_channel_layout;
        audio_hw_params->channels =  spec.channels;
        audio_hw_params->frame_size = av_samples_get_buffer_size(NULL, audio_hw_params->channels, 1, audio_hw_params->fmt, 1);
        audio_hw_params->bytes_per_sec = av_samples_get_buffer_size(NULL, audio_hw_params->channels, audio_hw_params->freq, audio_hw_params->fmt, 1);
        if (audio_hw_params->bytes_per_sec <= 0 || audio_hw_params->frame_size <= 0) {
            av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
            return -1;
        }
        return spec.size;
    }
    
    
    /**
     * 解码中断回调
     * @param ctx
     * @return
     */
    int CainPlayer::decode_interrupt_cb(void *ctx) {
        VideoState *is = (VideoState *)ctx;
        return is->abort_request;
    }
    
    /**
     * 判断媒体流中是否存在足够的裸数据包
     * @param st
     * @param stream_id
     * @param queue
     * @return
     */
    int CainPlayer::stream_has_enough_packets(AVStream *st, int stream_id, PacketQueue *queue) {
        return (stream_id < 0) || (queue->abort_request)
               || (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
               || (queue->nb_packets > MIN_FRAMES)
                  && (!queue->duration || av_q2d(st->time_base) * queue->duration > 1.0);
    }
    
    /**
     * 是否实时流
     * @param s
     * @return
     */
    int CainPlayer::is_realtime(AVFormatContext *s) {
        if (!strcmp(s->iformat->name, "rtp") || !strcmp(s->iformat->name, "rtsp")
           || !strcmp(s->iformat->name, "sdp")) {
            return 1;
        }
    
        if (s->pb && (!strncmp(s->filename, "rtp:", 4) || !strncmp(s->filename, "udp:", 4))) {
            return 1;
        }
        return 0;
    }
    
    
    /**
     * 视频刷新线程句柄
     * @param arg
     * @return
     */
    int CainPlayer::video_refresh_thread(void *arg) {
        CainPlayer *player = (CainPlayer *) arg;
        player->refreshVideo();
        return 0;
    }
    
    /**
     * 刷新画面
     */
    void CainPlayer::refreshVideo() {
        double remaining_time = 0.0;
        while (!is->abort_request) {
            if (remaining_time > 0.0) {
                av_usleep((int) (int64_t) (remaining_time * 1000000.0));
            }
            remaining_time = REFRESH_RATE;
            if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh)) {
                video_refresh(is, &remaining_time);
            }
        }
    }
    
    /**
     * 事件循环
     * @param cur_stream
     */
    void CainPlayer::event_loop(VideoState *cur_stream) {
        SDL_Event event;
        double incr, pos;
    
        bool exitLooper = 0;
        for (;;) {
            double x;
            switch (event.type) {
                case SDL_KEYDOWN:
                    switch (event.key.keysym.sym) {
                        case SDLK_ESCAPE:
                            // 退出
                        case SDLK_q:
                            do_exit(cur_stream);
                            break;
    
                            // 暂停/播放
                        case SDLK_p:
                        case SDLK_SPACE:
                            toggle_pause(cur_stream);
                            break;
    
                            // 静音
                        case SDLK_m:
                            toggle_mute(cur_stream);
                            break;
    
                            // 增加声音
                        case SDLK_KP_MULTIPLY:
                        case SDLK_0:
                            update_volume(cur_stream, 1, SDL_VOLUME_STEP);
                            break;
    
                            // 减少声音
                        case SDLK_KP_DIVIDE:
                        case SDLK_9:
                            update_volume(cur_stream, -1, SDL_VOLUME_STEP);
                            break;
    
                            // 下一帧
                        case SDLK_s: // S: Step to next frame
                            step_to_next_frame(cur_stream);
                            break;
    
                            // 向前10秒
                        case SDLK_LEFT:
                            incr = -10.0;
                            goto do_seek;
    
                            // 向后10秒
                        case SDLK_RIGHT:
                            incr = 10.0;
                            goto do_seek;
    
                            // 向后60秒
                        case SDLK_UP:
                            incr = 60.0;
                            goto do_seek;
    
                            // 向前1分钟
                        case SDLK_DOWN:
                            incr = -60.0;
                        do_seek:
                            if (seek_by_bytes) {
                                pos = -1;
                                if (pos < 0 && cur_stream->videoStreamIdx >= 0)
                                    pos = frame_queue_last_pos(&cur_stream->videoFrameQueue);
                                if (pos < 0 && cur_stream->audioStreamIdx >= 0)
                                    pos = frame_queue_last_pos(&cur_stream->audioFrameQueue);
                                if (pos < 0)
                                    pos = avio_tell(cur_stream->ic->pb);
                                if (cur_stream->ic->bit_rate)
                                    incr *= cur_stream->ic->bit_rate / 8.0;
                                else
                                    incr *= 180000.0;
                                pos += incr;
                                stream_seek(cur_stream, pos, incr, 1);
                            } else {
                                pos = get_master_clock(cur_stream);
                                if (isnan(pos))
                                    pos = (double)cur_stream->seek_pos / AV_TIME_BASE;
                                pos += incr;
                                if (cur_stream->ic->start_time != AV_NOPTS_VALUE && pos < cur_stream->ic->start_time / (double)AV_TIME_BASE)
                                    pos = cur_stream->ic->start_time / (double)AV_TIME_BASE;
                                stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0);
                            }
                            break;
    
                        default:
                            break;
                    }
                    break;
    
                case SDL_QUIT:
                case FF_QUIT_EVENT:
                    do_exit(cur_stream);
                    exitLooper = 1;
                    break;
                default:
                    break;
            }
    
            // 是否退出事件循环
            if (exitLooper) {
                break;
            }
        }
    }
    
    /**
     * 锁管理器
     * @param mtx
     * @param op
     * @return
     */
    int CainPlayer::lockmgr(void **mtx, enum AVLockOp op) {
        switch(op) {
            case AV_LOCK_CREATE:
                *mtx = MutexCreate();
                if(!*mtx) {
                    av_log(NULL, AV_LOG_FATAL, "MutexCreate(): %s\n", SDL_GetError());
                    return 1;
                }
                return 0;
    
            case AV_LOCK_OBTAIN:
                return !!MutexLock((Mutex*)*mtx);
    
            case AV_LOCK_RELEASE:
                return !!MutexUnlock((Mutex*)*mtx);
    
            case AV_LOCK_DESTROY:
                MutexDestroy((Mutex*)*mtx);
                return 0;
        }
        return 1;
    }
    
    /**
     * 播放
     * @param argc
     * @param argv
     * @return
     */
    int CainPlayer::play(int argc, char **argv) {
        int flags;
    
        av_log_set_flags(AV_LOG_SKIP_REPEATED);
    
        //注册所有解码器、解复用器和协议
        av_register_all();
        avformat_network_init();
    
        av_dict_set(&sws_dict, "flags", "bicubic", 0);
    
        signal(SIGINT , sigterm_handler); /* Interrupt (ANSI).    */
        signal(SIGTERM, sigterm_handler); /* Termination (ANSI).  */
    
        // 输入文件名
        char *input_filename = argv[1];
        if (!input_filename) {
            av_log(NULL, AV_LOG_FATAL, "An input file must be specified\n");
            exit(1);
        }
    
        if (display_disable) {
            video_disable = 1;
        }
        flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
    
        if (audio_disable) {
            flags &= ~SDL_INIT_AUDIO;
        } else {
            /* Try to work around an occasional ALSA buffer underflow issue when the
             * period size is NPOT due to ALSA resampling by forcing the buffer size. */
            if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))
                SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);
        }
        if (display_disable) {
            flags &= ~SDL_INIT_VIDEO;
        }
        if (SDL_Init (flags)) {
            av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());
            av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");
            exit(1);
        }
    
        SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
        SDL_EventState(SDL_USEREVENT, SDL_IGNORE);
    
        // 注册锁管理器
        if (av_lockmgr_register(lockmgr)) {
            av_log(NULL, AV_LOG_FATAL, "Could not initialize lock manager!\n");
            do_exit(NULL);
        }
    
        // 初始化裸数据包
        av_init_packet(&flush_pkt);
        flush_pkt.data = (uint8_t *)&flush_pkt;
    
        // 打开媒体流
        is = stream_open(input_filename);
        if (!is) {
            av_log(NULL, AV_LOG_FATAL, "Failed to initialize VideoState!\n");
            do_exit(NULL);
        }
    
        // 事件循环
        event_loop(is);
    
        return 0;
    }
    

    到这里,还不行,我们还缺少了 NDK的Log方法,里面的ALOG开头的方法都是我封装的,如下:
    native_log.h:

    #ifndef CAINCAMERA_NATIVE_LOG_H
    #define CAINCAMERA_NATIVE_LOG_H
    #include <android/log.h>
    
    #define JNI_DEBUG 1
    #define JNI_TAG "CainJni_ffmpeg"
    
    #define ALOGE(format, ...) if (JNI_DEBUG) { __android_log_print(ANDROID_LOG_ERROR, JNI_TAG, format, ##__VA_ARGS__); }
    #define ALOGI(format, ...) if (JNI_DEBUG) { __android_log_print(ANDROID_LOG_INFO,  JNI_TAG, format, ##__VA_ARGS__); }
    #define ALOGD(format, ...) if (JNI_DEBUG) { __android_log_print(ANDROID_LOG_DEBUG, JNI_TAG, format, ##__VA_ARGS__); }
    #define ALOGW(format, ...) if (JNI_DEBUG) { __android_log_print(ANDROID_LOG_WARN,  JNI_TAG, format, ##__VA_ARGS__); }
    #endif //CAINCAMERA_NATIVE_LOG_H
    

    然后,我们还发现,缺少几个方法,这几个方法是在ffmpeg源码中的cmdutils.c文件中的,我们不需要cmdutils.c整个文件,只需要少数几个方法而已,因此,这里我们直接将那几个方法移植过来就好:
    CmdUtils.h

    #ifndef CAINPLAYER_CMDUTILS_H
    #define CAINPLAYER_CMDUTILS_H
    
    
    #include "ffplay_def.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
                                    AVFormatContext *s, AVStream *st, AVCodec *codec);
    int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec);
    AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, AVDictionary *codec_opts);
    void print_error(const char *filename, int err);
    
    #ifdef __cplusplus
    };
    #endif
    
    #endif //CAINPLAYER_CMDUTILS_H
    

    CmdUtils.cpp:

    #include "CmdUtils.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /**
     * 过滤解码器属性
     * @param opts
     * @param codec_id
     * @param s
     * @param st
     * @param codec
     * @return
     */
    AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
                                    AVFormatContext *s, AVStream *st, AVCodec *codec) {
        AVDictionary    *ret = NULL;
        AVDictionaryEntry *t = NULL;
        int            flags = s->oformat ? AV_OPT_FLAG_ENCODING_PARAM
                                          : AV_OPT_FLAG_DECODING_PARAM;
        char          prefix = 0;
        const AVClass    *cc = avcodec_get_class();
    
        if (!codec)
            codec            = s->oformat ? avcodec_find_encoder(codec_id)
                                          : avcodec_find_decoder(codec_id);
    
        switch (st->codecpar->codec_type) {
            case AVMEDIA_TYPE_VIDEO:
                prefix  = 'v';
                flags  |= AV_OPT_FLAG_VIDEO_PARAM;
                break;
            case AVMEDIA_TYPE_AUDIO:
                prefix  = 'a';
                flags  |= AV_OPT_FLAG_AUDIO_PARAM;
                break;
            case AVMEDIA_TYPE_SUBTITLE:
                prefix  = 's';
                flags  |= AV_OPT_FLAG_SUBTITLE_PARAM;
                break;
        }
    
        while (t = av_dict_get(opts, "", t, AV_DICT_IGNORE_SUFFIX)) {
            char *p = strchr(t->key, ':');
    
            /* check stream specification in opt name */
            if (p)
                switch (check_stream_specifier(s, st, p + 1)) {
                    case  1: *p = 0; break;
                    case  0:         continue;
                    default:         break;
                }
    
            if (av_opt_find(&cc, t->key, NULL, flags, AV_OPT_SEARCH_FAKE_OBJ) ||
                !codec ||
                (codec->priv_class &&
                 av_opt_find(&codec->priv_class, t->key, NULL, flags,
                             AV_OPT_SEARCH_FAKE_OBJ)))
                av_dict_set(&ret, t->key, t->value, 0);
            else if (t->key[0] == prefix &&
                     av_opt_find(&cc, t->key + 1, NULL, flags,
                                 AV_OPT_SEARCH_FAKE_OBJ))
                av_dict_set(&ret, t->key + 1, t->value, 0);
    
            if (p)
                *p = ':';
        }
        return ret;
    }
    
    /**
     * 检查媒体流
     * @param s
     * @param st
     * @param spec
     * @return
     */
    int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) {
        int ret = avformat_match_stream_specifier(s, st, spec);
        if (ret < 0)
            av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
        return ret;
    }
    
    /**
     * 设置媒体流信息
     * @param s
     * @param codec_opts
     * @return
     */
    AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, AVDictionary *codec_opts) {
        int i;
        AVDictionary **opts;
    
        if (!s->nb_streams)
            return NULL;
        opts = (AVDictionary **) av_mallocz_array(s->nb_streams, sizeof(*opts));
        if (!opts) {
            av_log(NULL, AV_LOG_ERROR,
                   "Could not alloc memory for stream options.\n");
            return NULL;
        }
        for (i = 0; i < s->nb_streams; i++)
            opts[i] = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id,
                                        s, s->streams[i], NULL);
        return opts;
    }
    
    /**
     * 打印错误
     * @param filename
     * @param err
     */
    void print_error(const char *filename, int err) {
        char errbuf[128];
        const char *errbuf_ptr = errbuf;
    
        if (av_strerror(err, errbuf, sizeof(errbuf)) < 0)
            errbuf_ptr = strerror(AVUNERROR(err));
        av_log(NULL, AV_LOG_ERROR, "%s: %s\n", filename, errbuf_ptr);
    }
    
    #ifdef __cplusplus
    };
    #endif
    

    OK,到了这里,我们就将整个ffplay的播放器核心功能就移植过来了。但这样还不行,SDL还要用到main方法,因此我们需要添加一个这样的方法:
    CainPlayerController.cpp:

    #include "CainPlayer.h"
    
    int main(int argc, char **argv) {
        CainPlayer *player = new CainPlayer();
        int ret = player->play(argc, argv);
        delete player;
    
        return 0;
    }
    

    到这里,我们就将整个ffplay的核心功能移植过来了。编译运行,我们就可以看到,播放器成功运行了。
    缺点:
    这里只是将ffplay的播放器核心功能移植过来,去掉了字幕和AVFilter滤镜的方法。如果需要添加字幕和滤镜的话,你也可以在移植过程中,不去掉这些功能。
    ffplay在播放4K视频的时候,发现画面卡死了,这个估计是SDL的问题。因此,你支持4K视频的的话,肯定是要将整个SDL替换掉的,渲染效率不够。

    另外一个问题就是,这里仅仅是把播放器的核心功能移植过来,并没有考虑到控制逻辑的问题,因此,你无法使用点击返回、定位、调整音量大小等逻辑。这个其实是SDL的问题。

    本人并不太喜欢SDL的逻辑,在定制操作时,还要对SDL中src/core/android/SDL_android.c的源码动刀,个人觉得这个架构思路对移动端来说并不好,so能独立就独立。在移植过程中,本人就已经将SDL的锁和线程用自己实现的方法替换掉了。只有音频播放和视频帧渲染的方法没有替换掉,这里其实可以将整个SDL替换掉的,这样你就可以脱离SDL的束缚,根据实际的需求定制了,ijkplayer就是这样的一种思路,自己实现一个ijksdl的代码,将SDL的代码完全替换掉。
    不过个人觉得ijkplayer的文件太散乱了,而且每个版本的方法有可能发生变化。比如说,本人想要对视频添加滤镜,那么需修改的地方太多了,而且一旦升级版本之后,以前的代码就可能失效了。感觉ijkplayer 还是可以做得更好的。后续,有机会的话,本人将会介绍如何替换掉播放器中的SDL相关方法。

    相关文章

      网友评论

        本文标题:FFmpeg编程开发笔记 —— 基于 ffplay 的Andro

        本文链接:https://www.haomeiwen.com/subject/bvxgcftx.html