• 初始化调用avplayer学习笔记


    发一下牢骚和主题无关:

        Avplayer详细设计

        一.   视频显示库(video)

        1.      第三方环境

        DirectX9.0

        

        2.      对外接口

        2.1. 初始化 (d3d_init_video)

        EXPORT_API int d3d_init_video(struct vo_context *ctx, int w, int h, int pix_fmt);

        2.2. 渲染一帧 (d3d_render_one_frame)

        EXPORT_API int d3d_render_one_frame(struct vo_context *ctx, AVFrame* data, int pix_fmt, double pts);

        2.3. 重置画面巨细 (d3d_re_size)

        EXPORT_API void d3d_re_size(struct vo_context *ctx, int width, int height);

        2.4. (d3d_aspect_ratio)

        EXPORT_API void d3d_aspect_ratio(struct vo_context *ctx, int srcw, int srch, int enable_aspect);

        2.5. 刷新屏幕 (d3d_use_overlay)

        EXPORT_API int d3d_use_overlay(struct vo_context *ctx);

        2.6. 释放资源 (d3d_destory_render)

        EXPORT_API void d3d_destory_render(struct vo_context *ctx);

        3.      应用方法

        由zlplayer调用,对应用层透明。

        3.1.   视频显示结构体 (caller层)

        typedefstruct vo_context

        {

          int (*init_video)(structvo_context *vo_ctx, int w, int h, int pix_fmt);

          int(*render_one_frame)(struct vo_context *vo_ctx, AVFrame* data, int pix_fmt,double pts);

          void(*re_size)(struct vo_context *vo_ctx, int width, int height);

          void(*aspect_ratio)(struct vo_context *vo_ctx, int srcw, int srch, intenable_aspect);

          int(*use_overlay)(struct vo_context *vo_ctx);

          void(*destory_video)(struct vo_context *vo_ctx);

        void *priv;            //video库中真正的视频处理结构 ß(void*)(d3d = new d3d_render);

           void *user_data;     //窗口句柄

           void *user_ctx;      //user context

           float fps;               //frames per seconds ?

        }vo_context;

        Caller应用video 库的导出函数,为自己的vo_context结构中的函数指针赋值,然后应用。

        3.2.   vo_context::init_video

        调用函数: player_impl::video_render_thrd

        if(!inited && play->m_vo_ctx)

        {

           inited = 1;

        ret =play->m_vo_ctx->init_video(play->m_vo_ctx,

        play->m_video_ctx->width,

        play->m_video_ctx->height,

        play->m_video_ctx->pix_fmt);

           if (ret != 0)    inited = -1;

           else        play->m_play_status= playing;

        }

        通过线程的局部变量inited控制video render的初始化。

        当inited == 0时,调用m_vo_ctx->init_video初始化video render;

        准确初始化:inited ß 1;play->m_play_status ßplaying;

        初始化异常:inited ß-1;

        线程的其他部分,通过判断inited值来获得video render的初始化状态。

        3.3.   vo_context::render_one_frame

        调用函数:player_impl::video_render_thrd

        线程循环中,两处需要调用render_one_frame函数。

        (1) 渲染一个视频帧

        if(inited == 1 && play->m_vo_ctx)

        {

           play->m_vo_ctx->render_one_frame(play->m_vo_ctx,

        &video_frame,

        play->m_video_ctx->pix_fmt,av_curr_play_time(play));

           if (delay != 0)       Sleep(4);

        }

        (2) 播放器暂停,渲染黑屏               //实时播放,不需要渲染黑屏

        while(play->m_play_status == paused && inited == 1 &&play->m_vo_ctx && !play->m_abort)

        {

           play->m_vo_ctx->render_one_frame(play->m_vo_ctx,

        &video_frame,

        play->m_video_ctx->pix_fmt,av_curr_play_time(play));

           Sleep(16);

        }

        3.4.   vo_context::re_size

        调用函数:player_impl::win_wnd_proc

        CaseWM_SIZE:

        m_video->re_size(m_video, LOWORD(lparam),HIWORD(lparam));

        d3d库中没有实现re_size,但是在读RTP流时,是否需要根据SPSPPS信息,而实现该函数呢?

        根据屏幕巨细,而改变视频巨细,是否也应该实现此函数呢?

        注:Ddraw_render::re_size实现了。

        3.5.   vo_context::aspect_ratio

        无调用

        3.6.   vo_context::use_overlay

        调用函数:player_impl::win_paint(HWND hwnd,HDC hdc)

        if(m_avplay &&

           m_avplay->m_vo_ctx &&

           m_video->priv &&

           m_video->use_overlay(m_video) != -1)

        {    

        RECT client_rect;

           GetClientRect(hwnd, &client_rect);

           fill_rectange(hwnd, hdc, client_rect,client_rect);

        }

        3.7.   vo_context::destory_video

        调用函数:libav. Free_video_render(vo_context*ctx)

        //释放渲染器后,释放vo_context结构

        if(ctx->priv)

           ctx->destory_video(ctx);      

        free(ctx);

        free_video_render调用函数1:libav.configure(avplay *play, void *param, int type)

        case VIDEO_RENDER:

                  {

                         if (play->m_vo_ctx&& play->m_vo_ctx->priv)

                                free_video_render(play->m_vo_ctx);

                         play->m_vo_ctx =(vo_context*)param;

                  }

                  break;

        该函数存在风险free_video_render中,只是free了,没有对指针置NULL,那么这里就有可能再次释放已经释放了的结构。因此需要改进free_video_render函数

        free_video_render调用函数2:voidav_stop(avplay *play)

        停止播放器之前,释放资源。

        free_video_render调用函数3:player_impl::open()

        用于初始化异常时,释放已经初始化的结构。

        二.   视频解码显示库(libav)

        1.      第三方环境

        最新版ffmpeg

        2.      对外接口

        2.1. EXPORT_API source_context*alloc_media_source(int type, const char *addition,

        int addition_len, int64_t size);

        2.2. EXPORT_API voidfree_media_source(source_context *ctx);

        2.3. EXPORT_API ao_context*alloc_audio_render();

        2.4. EXPORT_API voidfree_audio_render(ao_context *ctx);

        2.5. 创建渲染器EXPORT_APIvo_context* alloc_video_render(void *user_data);

        2.6. 释放渲染器EXPORT_APIvoid free_video_render(vo_context *ctx);

        2.7. EXPORT_API demux_context*alloc_demux_context();

        2.8. EXPORT_API voidfree_demux_context(demux_context *ctx);

        2.9. 创建播放器EXPORT_APIavplay* alloc_avplay_context();

        2.10. 释放播放器EXPORT_APIvoid free_avplay_context(avplay *ctx);

        2.11. 初始化EXPORT_APIint initialize(avplay *play, source_context *sc);

        2.12. EXPORT_API int initialize_avplay(avplay*play, const char *file_name, int source_type,

                                                     demux_context*dc);

        2.13. 配置EXPORT_APIvoid configure(avplay *play, void *param, int type);

        2.14. 开始播放EXPORT_APIint av_start(avplay *play, double fact, int index);

        2.15. 等待结束EXPORT_APIvoid wait_for_completion(avplay *play);

        2.16. 停止播放.没调用EXPORT_API void av_stop(avplay *play);

        2.17. 暂停EXPORT_APIvoid av_pause(avplay *play);

        2.18. 重启EXPORT_APIvoid av_resume(avplay *play);

        2.19. 跳转EXPORT_APIvoid av_seek(avplay *play, double fact);

        2.20. EXPORT_API int av_volume(avplay *play,double l, double r);

        每日一道理
    爱心是一片照射在冬日的阳光,使贫病交迫的人感到人间的温暖;爱心是一泓出现在沙漠里的泉水,使濒临绝境的人重新看到生活的希望;爱心是一首飘荡在夜空的歌谣,使孤苦无依的人获得心灵的慰藉。

        2.21. EXPORT_API int audio_is_inited(avplay*play);

        2.22. EXPORT_API void av_mute_set(avplay*play, int s);

        2.23. 当前主时间EXPORT_APIdouble av_curr_play_time(avplay *play);

        2.24. 播放时长EXPORT_APIdouble av_duration(avplay *play);

        2.25. 销毁EXPORT_APIvoid av_destory(avplay *play);

        2.26. 打开帧率统计EXPORT_API void enable_calc_frame_rate(avplay *play);

        2.27. 打开码率统计EXPORT_API void enable_calc_bit_rate(avplay *play);

        2.28. EXPORT_API int current_bit_rate(avplay*play); // play->m_real_bit_rate
    2.29. EXPORT_API int current_frame_rate(avplay *play);

        2.30. EXPORT_API double buffering(avplay*play);

        2.31. EXPORT_API voidset_download_path(avplay *play, const char *save_path);

        2.32. EXPORT_API void set_youku_type(avplay*play, int type);

        2.33. EXPORT_API void blurring(AVFrame*frame,

                                              intfw, int fh, int dx, int dy, int dcx, int dcy);

        2.34. EXPORT_API void alpha_blend(AVFrame*frame, uint8_t *rgba,

                                                     intfw, int fh, int rgba_w, int rgba_h, int x, int y);

        2.35. EXPORT_API int logger_to_file(constchar* logfile);

        2.36. EXPORT_API int close_logger_file();

        2.37. EXPORT_API int logger(const char *fmt,...);

        3.      功能及应用方法

        3.1. source_context* alloc_media_source

        功能:根据输入参数,分配填充source_context结构。

        调用函数:BOOL player_impl::open(const char*movie, int media_type, int render_type)

        if(media_type == MEDIA_TYPE_FILE)

        {

           len = strlen(filename);

           m_source =alloc_media_source(MEDIA_TYPE_FILE, filename, len + 1, file_lentgh);  

           init_file_source(m_source);

        }

        3.2.  void free_media_source

        功能:释放source_context及真正的source结构。

        调用函数1:voidconfigure(avplay *play, void *param, int type)

        caseMEDIA_SOURCE:

        if (play->m_play_status == playing || play->m_play_status== paused)

                  return;    //不允许重新配置正在应用的播放器

           if (play->m_source_ctx)

           {

                  if (play->m_source_ctx&& play->m_source_ctx->priv)

                         play->m_source_ctx->close(play->m_source_ctx);

                  free_media_source(play->m_source_ctx);

                  play->m_source_ctx =(source_context*)param;

           }

        Configure函数,相当于环境重新配置函数

        调用函数2:libav.read_pkt_thrd

        在读数据包的线程循环退出之后,释放媒体源。

        调用函数3:player_impl::open

        当打开播放器失败时,需要释放媒体源。

        调用函数4:player_impl::close()

        if (m_avplay)

           {

                  ::av_destory(m_avplay);

                  m_avplay = NULL;       .

                  m_source = NULL;

                  m_cur_index = -1;

                  ::logger("closeavplay.\n");

                  return TRUE;

           }

           else

           {

                  if (m_source) // m_avplay已经不存在, 手动释放m_source.

                  {

                         free_media_source(m_source);

                         m_source = NULL;

                  }

           }

        从这里可以看出,player_impl::m_sourceplayer_impl::m_avplay.m_source_ctx是同一片区域,释放了一个,就不用释放另一个

        3.5. 创建渲染器vo_context* alloc_video_render(void*user_data)

        调用函数:player_impl::open(constchar *movie, int media_type, int render_type)

        3.6. 释放渲染器voidfree_video_render

        调用函数1:libav.void configure(avplay *play, void *param, int type)

        调用函数2:libav.void av_stop(avplay *play)

        调用函数3:player_impl::open失败时

        3.9. 创建播放器avplay* alloc_avplay_context()

        功能:分配avplay空间

        调用函数:player_impl::open(const char*movie, int media_type, int render_type)

        3.10.             释放播放器 void free_avplay_context(avplay*ctx)

        功能: 释放avplay空间

        调用函数:player_impl::open失败时。

        注:libav.av_stop用来停止avplay,释放其内部结构。而free_avplay_context只是简单free结构指针,适用于在结构初始化不成功时,直接释放。此时,avplay里面的相关结构还没有运行起来。

        3.11. 初始化intinitialize

        功能:ffmpeg相关的初始化,open_decoder, init_queue

        调用函数:player_impl::open

        3.13. 配置voidconfigure

        功能:利用函数参数重新初始化播放器环境。

        调用函数:player_impl::open

        初始化播放器、渲染器之后,configure。

        3.14. 开始播放intav_start

        功能:起各种线程

        调用函数:player_impl::play

        3.15. 等待结束voidwait_for_completion

        功能:

        while (play->m_play_status == playing ||play->m_play_status == paused){

                  Sleep(100);

        }

        调用函数:player_impl::wait_for_completion()àmain. void play_thread(void *param)

        3.16. 停止播放. 无调用av_stop

        流程:

        通知各个线程退出;

        等待线程退出后,释放资源;

        更改播放器状态;

        Avformat_network_deinit()

        调用函数1:libav. Av_destroy

        调用函数2:player_impl::stop()

        3.17. 暂停voidav_pause

        实现:play->m_play_status= paused;

        调用函数:player_impl::win_wnd_proc

        caseWM_RBUTTONDOWN:

        if (m_avplay && m_avplay->m_play_status == playing)

                                pause();

                         elseif (m_avplay && m_avplay->m_play_status == paused)

                                resume();

        3.18. 重启void av_resume

        实现:play->m_play_status= playing;

        调用函数:同av_pause

        3.19. 跳转av_seek

        实现:set各种seeking相关avplay成员

        调用函数1:libav. read_pkt_thrd(void *param)

           读线程开始之前,跳转到avplay.m_start_time

        调用函数2:player_impl::win_wnd_proc

        caseWM_LBUTTONDOWN:

        3.23. 当前主时间av_curr_play_time

        实现:如果同步到video,则play->m_video_current_pts_drift+ av_gettime() / 1000000.0f;

        3.24. 播放时长av_duration

        实现:(double)play->m_format_ctx->duration/ AV_TIME_BASE;

        功能:可帮助实现av_seek

        3.25. 销毁av_destroy

           if(play->m_play_status != stoped && play->m_play_status != inited)

           {

                  /*关闭数据源. */

                  if(play->m_source_ctx && play->m_source_ctx->priv)

                         play->m_source_ctx->close(play->m_source_ctx);

                  av_stop(play);

           }

           free(play);

        可以看出,在如下的播放状态中:

        typedef enum play_status{

           inited,playing, paused, completed, stoped

        } play_status;

        Playingpausedcompleted都是播放中的状态,都还有许多内部结构,在free avplay之前,需要av_stop(avplay).

        4. avplay结构成员

        4.1. 起始播放时间m_start_time

        在av_start()函数中赋值(play->m_start_time = fact;),默认参数为0。

        在read_pkt_thrd线程函数中,用以判断是否需要seek。

        4.2. 播放状态 m_play_status

        (1) 在av_start()函数中,create各个线程之后,play->m_play_status= playing

        Av_start()函数在主流程中调用。

        (2) 在av_stop()函数中,停止线程、释放播放器内部资源之后,play->m_play_status = stoped

        (3) 在av_pause()中,play->m_play_status = paused

        (4) 在av_resume()中,play->m_play_status = playing

        (5) 在read_pkt_thrd中,当av_read_frame()返回值<0时,play->m_play_status= completed

                                         当av_read_frame()返回值!<0时,play->m_play_status = playing

        (6) 在video_render_thrd,在播放器需初始化分支,初始化完毕,play->m_play_status = playing

        4.3. 终止标识符 m_abort

        (1) 在initialize函数中,open decoder之后,初始化为play->m_abort = TRUE;

        (2) 在initialize函数中,初始化全局变量flush_pkt/frm后,play->m_abort = FALSE;

        (3) 在av_stop函数中,首先play->m_abort = TRUE;

        (4) 在read_pkt_thrd中,退出线程循环(读完)后,线程函数返回之前,play->m_abort = TRUE

        4.4. 缓冲管理

        long volatilem_pkt_buffer_size;          //读取数据包占用的缓冲区巨细

           pthread_mutex_tm_buf_size_mtx;       //互斥量

           m_buffer;                                                 //当前缓冲巨细 占 最大缓冲巨细的 百分比%

           #defineMAX_PKT_BUFFER_SIZE   5242880

        (1) 在read_pkt_thrd线程函数中,

        在跳转分支,清空队列之后,m_pkt_buffer_size = 0;

           当读取数据包达到最大缓冲之后,让系统休眠:

        while(play->m_pkt_buffer_size > MAX_PKT_BUFFER_SIZE

        && !play->m_abort && !play->m_seek_req)

                  Sleep(32);

           当读取数据包之后,play->m_pkt_buffer_size+= packet.size; m_buffer = ….

        (2) 在video_dec_thrd线程函数中,

        当取出一个数据包之后,play->m_pkt_buffer_size -= pkt.size;

        5. 全局变量flush_pkt / flush_frm

        5.1. 在queue_init函数中,

        put_queue(q,(void *)&flush_pkt);  put_queue(q,(void*) &flush_frm);

           queue_init在initialize()函数中调用

        5.2. 在queue_flush函数中,

        if (pkt->pkt.data != flush_pkt.data)

                                av_free_packet(&pkt->pkt);

                         if(pkt->pkt.data[0] != flush_frm.data[0])

                                av_free(pkt->pkt.data[0]);

        当flush / clear 队列中的所有元素时,不能释放flush_pkt / flush_frm的元素(指针相同)。

           当queue_end()函数中,需调用queue_flush。在read_pkt_thrd线程函数中,需要seek时,需queue_flush.

        5.3. 在initialize函数中

        av_init_packet(&flush_pkt);  //初始化flush_pkt空间

        flush_pkt.data =“FLUSH”;

        flush_frm.data[0]= “FLUSH”;

        5.4. 在read_pkt_thrd函数中

           在seek分支,需要queue_flush以刷新当前队列,之后,put_queue(…, &flush_pkt);

        5.5. 在video_dec_thrd函数中

           在线程函数的循环中,get_queue之后,if(pkt.data == flush_pkt.data) {刷新缓冲区,置m_video_dq中的frame标识为1(跳转),初始化play的一些属性成员。} 即,从现在开始,以前解码的frame都要被seek,不再显示了。

        注:flush_pkt用来做队列开始元素标识的。每当队列刷新时,首先put flush_pkt /frm元素,以同步队列。如,当从m_video_q中取到flush_pkt时,也就是说flush_pkt后面的元素与以前的元素不连续了,那么m_video_dq中的所有frame需要跳过了

          

        另外,flush_frm只是一个标识,是不能被渲染显示的,所以,在video_render_thrd中,当遇到flush_frm时,需要跳过。

    文章结束给大家分享下程序员的一些笑话语录: 《诺基亚投资手机浏览器UCWEB,资金不详或控股》杯具了,好不容易养大的闺女嫁外国。(心疼是你养的吗?中国创业型公司创业初期哪个从国有银行贷到过钱?)

  • 相关阅读:
    c++ qt安装配置教程
    PKi系统
    IKE协议
    Kerberos
    RADIUS和Diameter
    RageFrame学习笔记:创建路由+导入layui
    TP6框架--EasyAdmin学习笔记:数据表添加新参数,如何强制清除缓存
    JS原生2048小游戏源码分享
    风场可视化学习笔记:openlayers
    vue3.0 demo代码记录
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3089442.html
Copyright © 2020-2023  润新知