初始化调用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,资金不详或控股》杯具了,好不容易养大的闺女嫁外国。(心疼是你养的吗?中国创业型公司创业初期哪个从国有银行贷到过钱?)

转载于:https://www.cnblogs.com/xinyuyuanm/archive/2013/05/20/3089442.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值