超級詳細的ffplay源碼註釋(一)

/*
基於ffmpeg實現的播放器
av_gettime_relative  獲取時間,微秒爲單位

音視頻同步:
假如是以音頻爲基準,視頻同步音頻的方式,那麼就是音頻在每播放一幀的時候,就去將當前的時間同步到時間軸,視頻參考時間軸做調整

時間基:
時間基就是最小的時間刻度,時間戳就是在此最小刻度的基礎上記錄的時間量

SDL_LockMutex  加鎖

*/

#include "pch.h"
#include <iostream>
#include <Windows.h>

//增加的配置文件
#include "config.h"

//windows sdk自帶的文件
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <limits.h>
#include <signal.h>


//ffmpeg的頭文件
extern "C"
{
#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 "libavutil/display.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"
#if CONFIG_AVFILTER
# include "libavfilter/avfilter.h"
# include "libavfilter/buffersink.h"
# include "libavfilter/buffersrc.h"
#endif
}

//sdl的頭文件
#include "SDL/SDL.h"
#include "SDL/SDL_thread.h"

//需要的庫文件
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libpostproc.lib")
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libswresample.lib")
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libavcodec.lib")  
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libavformat.lib")  
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libavfilter.lib")  
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libavutil.lib")  
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libswscale.lib") 
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libavdevice.lib")
#pragma comment(lib,"../FFMPEG-master/FFMPEG/msvc/lib/x86/libsdl2.lib")
#pragma comment(lib,"Psapi.lib")

#define _CRT_SECURE_NO_WARNINGS

/* 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

/* no AV correction is done if too big error */
#define AV_NOSYNC_THRESHOLD 10.0
/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
#define AUDIO_DIFF_AVG_NB   20
/* maximum audio speed change to get correct sync */
#define SAMPLE_CORRECTION_PERCENT_MAX 10

#define FF_QUIT_EVENT    (SDL_USEREVENT + 2)

#define MAX_QUEUE_SIZE (15 * 1024 * 1024)

#define MIN_FRAMES 25

//音視頻同步,默認以音頻爲基準
enum {
    AV_SYNC_AUDIO_MASTER, /* default choice */
    AV_SYNC_VIDEO_MASTER,
    AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */
};

enum ShowMode {
    SHOW_MODE_NONE = -1,//顯示窗口自適應
    SHOW_MODE_VIDEO = 0,//顯示視頻
    SHOW_MODE_WAVES,//顯示音頻圖形
    SHOW_MODE_RDFT, //自適應濾波器
    SHOW_MODE_NB
};

//程序的名稱
const char program_name[] = "MyPlayer";


//一個空的packet, 解碼過程中碰到這種Pcket就會調用avcodec_flush_buffers(d->avctx)清空解碼器,
//在seek之後,通過往PacketQueue隊列種加這種Packet,來通知解碼器,代碼如下:
//packet_queue_flush(&is->audioq);
//packet_queue_put(&is->audioq, &flush_pkt)
//插入flush_pkt的同時會把serrial加一,可以知道一個新的播放序列開始了
static AVPacket flush_pkt;

static SDL_Window *window;
static SDL_Renderer *renderer;
static SDL_RendererInfo renderer_info = { 0 };
static SDL_AudioDeviceID audio_dev;//打開的音頻設備的ID


#if CONFIG_AVFILTER
static const char **vfilters_list = NULL;
static int nb_vfilters = 0;
static char *afilters = NULL;
#endif

static int show_status = 1;//命令行窗口是否顯示相關文件信息

/* options specified by the user */
static const char *input_filename;
static AVInputFormat *file_iformat;
static int startup_volume = 100;
static int av_sync_type = AV_SYNC_AUDIO_MASTER;
static int genpts = 0;//生成缺失的pts,即使需要解析未來幀才知道
static int find_stream_info = 0;//是否查找流中的信息
static int seek_by_bytes = -1;//不同視頻格式快進方式不同,是否是按字節方式快進,正常都是0
static const char *window_title;
static int64_t start_time = AV_NOPTS_VALUE;// 10000000; 從視頻的什麼位置開始播放,單位微秒
static int64_t duration = AV_NOPTS_VALUE; // 10000000;播放多長時間,單位微秒
static const char* wanted_stream_spec[AVMEDIA_TYPE_NB] = { 0 };//添加想要的流類型,默認有五種流類型,全部設置爲0,則默認只解析音頻,視頻和字幕,此參數可以去除

static int video_disable = 0;//禁止視頻
static int audio_disable = 0;//禁止音頻
static int borderless = 0;//窗口是否有邊框
static int subtitle_disable = 0;
static int default_width = 640;//默認渲染圖像寬度
static int default_height = 480;//默認渲染圖像高度
static int screen_width = 320;//強制的窗口寬度
static int screen_height = 240;//強制的窗口高度
ShowMode show_mode = SHOW_MODE_NONE;//默認顯示哪種信息,視頻,音頻
static int lowres = 0;//支持的低分辨率的最大值
static const char *audio_codec_name;//強制要使用的音頻解碼器的名字,默認不設置的話,使用的是文件流中一樣的解碼器
static const char *subtitle_codec_name;//強制要使用的字幕解碼器的名字,默認不設置的話,使用的是文件流中一樣的解碼器
static const char *video_codec_name;//強制要使用的視頻解碼器的名字,默認不設置的話,使用的是文件流中一樣的解碼器
static int fast = 0;//允許一些非標準的加速編碼方法

static int decoder_reorder_pts = -1;//解碼後,視頻時間戳的獲取方式

static int framedrop = -1;//允許丟幀標誌,當framedrop爲0時不允許丟棄幀,否則都是允許丟棄幀
static int autorotate = 1;
static int infinite_buffer = -1;
static int loop = 1;//循環播放的次數
static int autoexit;

/* current context */
static int is_full_screen = 0;//設置播放窗口全屏
static int64_t audio_callback_time;


AVDictionary *sws_dict;
AVDictionary *swr_opts;
AVDictionary *format_opts, *codec_opts, *resample_opts;

//程序退出
static void sigterm_handler(int sig)
{
    exit(123);
}

//打印版權/配置/版本信息
static void print_all_info()
{
    av_log(NULL, AV_LOG_INFO, "\n");
    av_log(NULL, AV_LOG_INFO, "%sbuilt with %s\n", " ", CC_IDENT);
    av_log(NULL, AV_LOG_INFO, "%sconfiguration: " FFMPEG_CONFIGURATION "\n", " ");

    unsigned int version = avcodec_version();
    av_log(NULL, AV_LOG_INFO,
        "%slib%-11s %2d.%3d.%3d / %2d.%3d.%3d\n", 
        " ", "avcodec", 
        LIBAVCODEC_VERSION_MAJOR,
                   LIBAVCODEC_VERSION_MINOR,
                   LIBAVCODEC_VERSION_MICRO,
                   AV_VERSION_MAJOR(version), AV_VERSION_MINOR(version),
                   AV_VERSION_MICRO(version));       
}

//打印用法
static void show_usage(void)
{
    av_log(NULL, AV_LOG_INFO, "Simple media player\n");
    av_log(NULL, AV_LOG_INFO, "usage: %s input_file\n", program_name);
    av_log(NULL, AV_LOG_INFO, "\n");

    
}

#ifdef _WIN32
#undef main /* SDL中有預定義main,我們要用自己的main,所以取消SDL中的定義 */
#endif

//時鐘,有三種時鐘,視頻時鐘,音頻時鐘,外部時鐘
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;    //指向對應的PacketQueue中的serial,外部時鐘指向自身時鐘的serial/* pointer to the current packet queue serial, used for obsolete clock detection */// 指向當前數據包隊列序列的指針,用於過時的時鐘檢測
    int clockType;//增加一個時鐘類型,0視頻時鐘,1音頻時鐘,2外部時鐘
} 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 MyAVPacketList {
    AVPacket pkt;
    struct MyAVPacketList *next;
    int serial;
} MyAVPacketList;

//循環隊列,其中存放加入進來的Packet
typedef struct PacketQueue {
    MyAVPacketList *first_pkt, //頭節點指針
        *last_pkt;//尾節點指針
    int nb_packets;//總共讀取的Packet包數,不包含已經取出的
    int size;//當前隊列中的Packet佔用的空間大小,包含指向的空間和本身的空間,不包含已經取出的
    int64_t duration;//當前隊列中的packet的播放總時間,不包含已經取出的
    int abort_request;//放棄請求
    int serial;//序列號,快進,快退,循環從頭播放都會導致序列號加一。即一次連續播放的編號,每開始一次連續的播放都有唯一的序列號
    SDL_mutex *mutex;
    SDL_cond *cond;
} PacketQueue;

#define VIDEO_PICTURE_QUEUE_SIZE 3//視頻FrameQueue中最大3個
#define SUBPICTURE_QUEUE_SIZE 16//字幕的FrameQueue中最大放置16個
#define SAMPLE_QUEUE_SIZE 9//音頻的FrameQueue中最大放置9個
#define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))

/* 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 const struct TextureFormatEntry {
    enum AVPixelFormat format;
    int texture_fmt;
} sdl_texture_format_map[] = {
    { AV_PIX_FMT_RGB8,           SDL_PIXELFORMAT_RGB332 },
    { AV_PIX_FMT_RGB444,         SDL_PIXELFORMAT_RGB444 },
    { AV_PIX_FMT_RGB555,         SDL_PIXELFORMAT_RGB555 },
    { AV_PIX_FMT_BGR555,         SDL_PIXELFORMAT_BGR555 },
    { AV_PIX_FMT_RGB565,         SDL_PIXELFORMAT_RGB565 },
    { AV_PIX_FMT_BGR565,         SDL_PIXELFORMAT_BGR565 },
    { AV_PIX_FMT_RGB24,          SDL_PIXELFORMAT_RGB24 },
    { AV_PIX_FMT_BGR24,          SDL_PIXELFORMAT_BGR24 },
    { AV_PIX_FMT_0RGB32,         SDL_PIXELFORMAT_RGB888 },
    { AV_PIX_FMT_0BGR32,         SDL_PIXELFORMAT_BGR888 },
    { AV_PIX_FMT_NE(RGB0, 0BGR), SDL_PIXELFORMAT_RGBX8888 },
    { AV_PIX_FMT_NE(BGR0, 0RGB), SDL_PIXELFORMAT_BGRX8888 },
    { AV_PIX_FMT_RGB32,          SDL_PIXELFORMAT_ARGB8888 },
    { AV_PIX_FMT_RGB32_1,        SDL_PIXELFORMAT_RGBA8888 },
    { AV_PIX_FMT_BGR32,          SDL_PIXELFORMAT_ABGR8888 },
    { AV_PIX_FMT_BGR32_1,        SDL_PIXELFORMAT_BGRA8888 },
    { AV_PIX_FMT_YUV420P,        SDL_PIXELFORMAT_IYUV },
    { AV_PIX_FMT_YUYV422,        SDL_PIXELFORMAT_YUY2 },
    { AV_PIX_FMT_UYVY422,        SDL_PIXELFORMAT_UYVY },
    { AV_PIX_FMT_NONE,           SDL_PIXELFORMAT_UNKNOWN },
};


typedef struct AudioParams {
    int freq;//採樣率
    int channels;//通道數
    int64_t channel_layout;
    enum AVSampleFormat fmt;
    int frame_size;//單個樣本佔用的大小,比如雙通道16位佔用4字節
    int bytes_per_sec;//每秒的數據量,採樣率*單個樣本的大小,比如48000*4=192000
} AudioParams;

typedef struct FrameQueue {
    Frame queue[FRAME_QUEUE_SIZE]; //是存儲Frame的數組
    int rindex;//是讀幀數據索引, 相當於是隊列的隊首
    int windex;//是寫幀數據索引, 相當於是隊列的隊尾
    int size;//是存儲在這個隊列的Frame的數量
    int max_size;//是可以存儲Frame的最大數量
    int keep_last;//這個變量的含義,據我分析, 是用來判斷隊列是否保留正在顯示的幀(Frame)
    int rindex_shown;//表示當前是否有幀在顯示
    SDL_mutex *mutex;
    SDL_cond *cond;
    PacketQueue *pktq;//指向各自數據包(ES包)的隊列
} FrameQueue;

typedef struct Decoder {
    AVPacket pkt;
    PacketQueue *queue;
    AVCodecContext *avctx;
    int pkt_serial;//序列號,快進,快退,循環從頭播放都會導致序列號加一。即一次連續播放的編號,每開始一次連續的播放都有唯一的序列號
    int finished;
    int packet_pending;
    SDL_cond *empty_queue_cond;
    int64_t start_pts;
    AVRational start_pts_tb;
    int64_t next_pts;
    AVRational next_pts_tb;
    SDL_Thread *decoder_tid;
} Decoder;

typedef struct VideoState {
    SDL_Thread *read_tid;
    AVInputFormat *iformat;
    int abort_request;//是否要中斷文件讀取
    int force_refresh;//需要進行強制刷新的標誌
    int paused;//當前的暫停狀態
    int last_paused;
    int queue_attachments_req;

    int seek_req;//是否要進行seek的請求標誌,進行seek之前會設置爲1,seek一次後會自動設置爲0
    int seek_flags;//seek的方式,按字節或者按時間
    int64_t seek_pos;//要seek到的位置,單位微秒或者字節數(要看seek的方式)
    int64_t seek_rel;//當前位置和要seek到的位置的距離,單位微秒或者字節數(要看seek的方式)

    int read_pause_return;//暫停讀取流的返回的錯誤碼
    AVFormatContext *ic;//讀取文件流的AVFormatContext上下文
    int realtime;//是否是實時流

    Clock audclk;//音頻時鐘
    Clock vidclk;//視頻時鐘
    Clock extclk;//外部時鐘

    FrameQueue pictq;//視頻的Frame隊列,解碼後的數據
    FrameQueue subpq;//字幕的Frame隊列,解碼後的數據
    FrameQueue sampq;//音頻的Frame隊列,解碼後的數據

    PacketQueue audioq;//音頻數據包
    PacketQueue subtitleq;//字幕數據包
    PacketQueue videoq;//視頻數據包

    Decoder auddec;//音頻解碼器
    Decoder viddec;//視頻解碼器
    Decoder subdec;

    int audio_stream;//音頻流的流序號
    AVStream *audio_st;//音頻流

    int av_sync_type;//音視頻的同步方式

    double audio_clock;
    int audio_clock_serial;
    double audio_diff_cum; /* used for AV difference average computation */
    double audio_diff_avg_coef;//初始化大約0.8
    double audio_diff_threshold;//初始化大約0.4
    int audio_diff_avg_count;//用於累加
    
    
    int audio_hw_buf_size;//打開音頻設備成功後,返回的buffer大小
    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;//解碼出的數據尚未放入設備緩存的數據量,也即待寫入的數據量
    int audio_volume;//設置音量
    int muted;//靜音
    struct AudioParams audio_src;//源音頻參數
#if CONFIG_AVFILTER
    struct AudioParams audio_filter_src;
#endif
    struct AudioParams audio_tgt;//目標音頻參數
    struct SwrContext *swr_ctx;
    int frame_drops_early;
    int frame_drops_late;

    ShowMode show_mode;
    int16_t sample_array[SAMPLE_ARRAY_SIZE];
    int sample_array_index;
    int last_i_start;
    RDFTContext *rdft;
    int rdft_bits;
    FFTSample *rdft_data;
    int xpos;
    double last_vis_time;
    SDL_Texture *vis_texture;
    SDL_Texture *sub_texture;
    SDL_Texture *vid_texture;

    int subtitle_stream;//字幕流的流序號
    AVStream *subtitle_st;//字幕流
    

    double frame_timer;//記錄當前幀實際播放到的時間
    double frame_last_returned_time;
    double frame_last_filter_delay;
    int video_stream;//視頻流的流序號
    AVStream *video_st;//視頻流

    double max_frame_duration;      // 最大幀顯示時間 默認算出來是3600 maximum duration of a frame - above this, we consider the jump a timestamp discontinuity
    struct SwsContext *img_convert_ctx;
    struct SwsContext *sub_convert_ctx;
    int eof;//是否到文件結尾

    char *filename;
    int width, height, xleft, ytop;
    int step;//步進的標誌,進行步進操作,按鍵盤S就是步進操作

#if CONFIG_AVFILTER
    int vfilter_idx;
    AVFilterContext *in_video_filter;   // the first filter in the video chain
    AVFilterContext *out_video_filter;  // the last filter in the video chain
    AVFilterContext *in_audio_filter;   // the first filter in the audio chain
    AVFilterContext *out_audio_filter;  // the last filter in the audio chain
    AVFilterGraph *agraph;              // audio filter graph
#endif

    int last_video_stream, last_audio_stream, last_subtitle_stream;

    SDL_cond *continue_read_thread;//讀線程的條件變量
} VideoState;

//////////////////////////////////////////////////////////////
//讀取數據
static void packet_queue_abort(PacketQueue *q)
{
    SDL_LockMutex(q->mutex);

    q->abort_request = 1;

    SDL_CondSignal(q->cond);

    SDL_UnlockMutex(q->mutex);
}
static void frame_queue_signal(FrameQueue *f)
{
    SDL_LockMutex(f->mutex);
    SDL_CondSignal(f->cond);
    SDL_UnlockMutex(f->mutex);
}

//清空隊列q中的所有數據
static void packet_queue_flush(PacketQueue *q)
{
    MyAVPacketList *pkt, *pkt1;

    SDL_LockMutex(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;
    SDL_UnlockMutex(q->mutex);
}

static void decoder_abort(Decoder *d, FrameQueue *fq)
{
    packet_queue_abort(d->queue);
    frame_queue_signal(fq);
    SDL_WaitThread(d->decoder_tid, NULL);
    d->decoder_tid = NULL;
    packet_queue_flush(d->queue);
}

static void decoder_destroy(Decoder *d) {
    av_packet_unref(&d->pkt);
    avcodec_free_context(&d->avctx);
}

static void 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->auddec, &is->sampq);
        SDL_CloseAudioDevice(audio_dev);
        decoder_destroy(&is->auddec);
        swr_free(&is->swr_ctx);
        av_freep(&is->audio_buf1);
        is->audio_buf1_size = 0;
        is->audio_buf = NULL;

        if (is->rdft) {
            av_rdft_end(is->rdft);
            av_freep(&is->rdft_data);
            is->rdft = NULL;
            is->rdft_bits = 0;
        }
        break;
    case AVMEDIA_TYPE_VIDEO:
        decoder_abort(&is->viddec, &is->pictq);
        decoder_destroy(&is->viddec);
        break;
    case AVMEDIA_TYPE_SUBTITLE:
        decoder_abort(&is->subdec, &is->subpq);
        decoder_destroy(&is->subdec);
        break;
    default:
        break;
    }

    ic->streams[stream_index]->discard = AVDISCARD_ALL;
    switch (codecpar->codec_type) {
    case AVMEDIA_TYPE_AUDIO:
        is->audio_st = NULL;
        is->audio_stream = -1;
        break;
    case AVMEDIA_TYPE_VIDEO:
        is->video_st = NULL;
        is->video_stream = -1;
        break;
    case AVMEDIA_TYPE_SUBTITLE:
        is->subtitle_st = NULL;
        is->subtitle_stream = -1;
        break;
    default:
        break;
    }
}

static void packet_queue_destroy(PacketQueue *q)
{
    packet_queue_flush(q);
    SDL_DestroyMutex(q->mutex);
    SDL_DestroyCond(q->cond);
}

//取消引用幀引用的所有緩衝區並重置幀字段,釋放給定字幕結構中的所有已分配數據
static void frame_queue_unref_item(Frame *vp)
{
    av_frame_unref(vp->frame);
    avsubtitle_free(&vp->sub);
}

#if CONFIG_AVFILTER
static int opt_add_vfilter(void *optctx, const char *opt, const char *arg)
{
    //GROW_ARRAY(vfilters_list, nb_vfilters);
    //vfilters_list[nb_vfilters - 1] = arg;
    return 0;
}
#endif


//釋放Frame,釋放互斥鎖和互斥量
static 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);
    }
    SDL_DestroyMutex(f->mutex);
    SDL_DestroyCond(f->cond);
}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章