如何基于FFMPEG和SDL写一个少于1000行代码的视频播放器

 

 

http://blog.csdn.net/eplaylity/archive/2008/12/05/3454431.aspx

http://www.cnblogs.com/konyel/tag/SDL+Guide+%E4%B8%AD%E6%96%87%E8%AF%91%E7%89%88/

ffmpeg文档http://blog.sina.com.cn/s/blog_46dc65a90100a91b.html

http://dranger.com/ffmpeg/ffmpeg.html

VLC核心

功能部份:

VLC媒体播放器的核心是libvlc ,它提供了界面,应用处理功能,如播放列表管理,音频和视频解码和输出,线程系统。所有libvlc源文件设在的/src目录及其子目录:

# config/ :从命令行和配置文件加载配置,提供功能模块的读取和写入配置
# control/: 提供动作控制功能,如播放/暂停,音量管理,全屏,日志等。
# extras/: 大多是平台的特殊代码
# modules/: 模块管理
# network/: 提供网络接口(socket管理,网络错误等)
# osd/: 显示屏幕上的操作
# test/: libvlc测试模块
# text/: 字符集
# interface/ : 提供代码中可以调用的接口中,如按键后硬件作出反应。
# playlist/: 管理播放功能,如停止,播放,下一首,随机播放等
# input/: 建立并读取一个输入流,并且分离其中的音频和视频,然后把分离好的音频流和视频流发送给解码器.
# video_output/ : 初始化视频播放器,把从解码器得到的视频画面转化格式(从YUV 转为 RGB)然后播放它们
# audio_output/ : 初始化音频混合器,即设置正确的同步频率,并对从解码器传来的音频流重新取样
# stream_output/: 输出音频流和视频流到网络
# misc/: libvlc使用的其他部分功能 ,如线程系统,消息队列, CPU的检测,对象查找系统,或平台的特定代码。

模块部份:

VLC媒体播放器的模块部份,在/modules的子目录下(详细说明可以参考其下的List文件),这些模块只在程序载入它们时有效.每一个模块,可提供不同的功能,它们会适合的特定文件或某一特定的环境.此外,audio_output/video_output/interface 模块都写成了可跨平台的代码,方便支持新的平台(如beos或服务Mac OS X ) 。

插件模块可以在 src/modules.c 和 include/vlc_modules*.h 提供函数中,动态加载和卸载

LibVLC可以将模块直接插入到应用程序中,例如不支持动态加载代码的操作系统.模块静态插入到应用程序叫内建.


VLC框架分析

1.vlc.c 只是入口程序

2.Libvlc.c 是各个模块的结合点,这要是对接口编程

  • Vlc_Create(): 两个重要的数据结构:libvlc_t & vlc_t , 所有的参数传递都在这里面
  • Vlc_Init(): 初始化参数, module_bank
  • Vlc_AddInf(): 添加module

3./src/misc/configure.c 命令行参数和参数文件分析
参数文件是~/.vnc/vlcrc。其中可以设置log文件的位置

4./include/ 所有头文件的集合

5./src/interface/Interface.h 所有module的集合

6./src/misc/Modules.c
其中module_t * __module_Need( vlc_object_t *p_this, const char *psz_capability,
const char *psz_name, vlc_bool_t b_strict ) 方法是寻找合适的interface
如果找到合适的,就调用AllocatePlugin()动态的分配一个。

7.how to link to different modules without OOP

 

 

vlc网络数据流接收处理过程分析

 

网络数据流接收处理分析

1、在input.c(src/input)文件中的主线程循环

      Thread in charge of processing the network packets and demultiplexing

RunThread( input_thread_t *p_input )

{

          InitThread( p_input ) ;

…………………………………………………….

     input_SelectES( p_input, p_input->stream.p_newly_selected_es );

             …………………………………………………….

      /* Read and demultiplex some data. */

    i_count = p_input->pf_demux( p_input );

 

}

2、在下列函数中:

  1. 分离出access , demux , name字符串 ;
  2. 根据分离出的access 字符串通过module_Need函数找到acess 指针模块;
  3. 根据分离出的demux 字符串通过module_Need函数找到demux 指针模块;

static int InitThread( input_thread_t * p_input )

{

     msg_Dbg( p_input, "access `%s', demux `%s', name `%s'",

             p_input->psz_access, p_input->psz_demux, p_input->psz_name );

 

    /* Find and open appropriate access module */

    p_input->p_access = module_Need( p_input, "access",

                                     p_input->psz_access, VLC_TRUE );

…………………………………………………….

while( !input_FillBuffer( p_input ) )

…………………………………………………….

    /* Find and open appropriate demux module */

    p_input->p_demux =

        module_Need( p_input, "demux",

                     (p_input->psz_demux && *p_input->psz_demux) ?

                     p_input->psz_demux : "$demux",

                     (p_input->psz_demux && *p_input->psz_demux) ?

                     VLC_TRUE : VLC_FALSE );

…………………………………………………….

}

3、在ps.c (module/demux/mpeg)文件中

a.通过消息映射宏赋值启动函数Activate;

b.通过函数Activate赋值p_input->pf_demux = Demux;

c. 通过函数module_Need( p_input, "mpeg-system", NULL, 0 ) 激活p_input->p_demux_data->mpeg.pf_read_ps( p_input, &p_data )函数(pf_read_ps);

d.在InitThread函数中激活;

 

        static int Activate( vlc_object_t * p_this )

{

      /* Set the demux function */

p_input->pf_demux = Demux;

p_input->p_private = (void*)&p_demux->mpeg;

    p_demux->p_module = module_Need( p_input, "mpeg-system", NULL, 0 );

}

4、在system.c (module/demux/mpeg)文件中

         赋值解码模块mpeg_demux_t的成员函数;

     static int Activate ( vlc_object_t *p_this )

{

    static mpeg_demux_t mpeg_demux =

                    { NULL, ReadPS, ParsePS, DemuxPS, ReadTS, DemuxTS };

    mpeg_demux.cur_scr_time = -1;

    memcpy( p_this->p_private, &mpeg_demux, sizeof( mpeg_demux ) );

 

    return VLC_SUCCESS;

}

并且申明函数static ssize_t ReadPS( input_thread_t * p_input, data_packet_t ** pp_data );

 

5、在ps.c (module/demux/mpeg)文件中

Demux( input_thread_t * p_input )

{

i_result = p_input->p_demux_data->mpeg.pf_read_ps( p_input, &p_data );

      p_input->p_demux_data->mpeg.pf_demux_ps( p_input, p_data );

}

进行读取数据和分离工作;

6、在system.c (module/demux/mpeg)文件中

数据走向图如下

ReadPS-> PEEK-> input_Peek(src/input/input_ext-plugins.c)-> input_FillBuffert 通过 i_ret = p_input->pf_read( p_input,

                              (byte_t *)p_buf + sizeof(data_buffer_t)

                               + i_remains,

                              p_input->i_bufsize );

input_thread_t结构的pf_read函数成员如果是为udp.c(modules/access)的RTPChoose函数

则在开启access(UDP 模块)时通过module_need 激活;

激活网络读数据模块 RTPChoose(modules/access/ udp.c)->Read->net_Read(src/misc/net.c);

 

7、在input_programs.c(src/input)文件中

         运行解码器对ES流解码

   int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )

{

      p_es->p_dec = input_RunDecoder( p_input, p_es );

  

}

input_SelectES(src/input/input_programs.c->input_RunDecodersrc/input/input_dec.c->DecoderThread->DecoderDecode ->vout_DisplayPicture

VLC程序宏及线程分析

第一部分 变量及宏定义
1.消息映射宏
vlc_module_begin();
…………………..
vlc_module_end();
2.结构中包含函数
struct input_thread_t
{
VLC_COMMON_MEMBERS

/* Thread properties */
vlc_bool_t b_eof;
vlc_bool_t b_out_pace_control;

/* Access module */
module_t * p_access;
ssize_t (* pf_read ) ( input_thread_t *, byte_t *, size_t );
int (* pf_set_program )( input_thread_t *, pgrm_descriptor_t * );
int (* pf_set_area )( input_thread_t *, input_area_t * );
void (* pf_seek ) ( input_thread_t *, off_t );
}
3.宏与换行符妙用
#define VLC_COMMON_MEMBERS /
/** /name VLC_COMMON_MEMBERS /
* these members are common for all vlc objects /
*/ /
/**@{*/ /
int i_object_id; /
int i_object_type; /
char *psz_object_type; /
char *psz_object_name; /
/
/** Just a reminder so that people don't cast garbage */ /
int be_sure_to_add_VLC_COMMON_MEMBERS_to_struct; /
/**@}*/

#define VLC_OBJECT( x ) /
((vlc_object_t *)(x))+
0*(x)->be_sure_to_add_VLC_COMMON_MEMBERS_to_struct

struct vlc_object_t
{
VLC_COMMON_MEMBERS
};//定义一个结构来使用宏定义的公共成员

4.定义导出函数
#ifndef __PLUGIN__
# define VLC_EXPORT( type, name, args ) type name args
#else
# define VLC_EXPORT( type, name, args ) struct _u_n_u_s_e_d_
extern module_symbols_t* p_symbols;
#endif
5.定义回调函数

typedef int ( * vlc_callback_t ) ( vlc_object_t *, /* variable's object */
char const *, /* variable name */
vlc_value_t, /* old value */
vlc_value_t, /* new value */

void * ); /* callback data */

6.函数作为参数的定义方式
Int Fun(int n,int (*pf)(int ,int),char *pstr)
{ int j =10;
pf(n,j);
}

7.回调函数的声明
必须声明为global,或者static

Int vlc_callback_t (int ,int)
{。。。。。。。。。。。}

8.回调函数的使用
Fun(0, vlc_callback_t,”test”);

9.函数表达式
#define input_BuffersInit(a) __input_BuffersInit(VLC_OBJECT(a))
void * __input_BuffersInit( vlc_object_t * );

#define module_Need(a,b,c,d) __module_Need(VLC_OBJECT(a),b,c,d)
VLC_EXPORT( module_t *, __module_Need, ( vlc_object_t *, const char *, const char *, vlc_bool_t ) );

10.定义函数
/* Dynamic array handling: realloc array, move data, increment position */
#define INSERT_ELEM( p_ar, i_oldsize, i_pos, elem ) /
do /
{ /
if( i_oldsize ) /
{ /
(p_ar) = realloc( p_ar, ((i_oldsize) + 1) * sizeof( *(p_ar) ) ); /
} /
else /
{ /
(p_ar) = malloc( ((i_oldsize) + 1) * sizeof( *(p_ar) ) ); /
} /
if( (i_oldsize) - (i_pos) ) /
{ /
memmove( (p_ar) + (i_pos) + 1, /
(p_ar) + (i_pos), /
((i_oldsize) - (i_pos)) * sizeof( *(p_ar) ) ); /
} /
(p_ar)[i_pos] = elem; /
(i_oldsize)++; /
} /
while( 0 )


应用为:
INSERT_ELEM( p_new->p_libvlc->pp_objects,
p_new->p_libvlc->i_objects,
p_new->p_libvlc->i_objects,
p_new );


11.改变地址的方式传递其值
stream_t *input_StreamNew( input_thread_t *p_input )
{ stream_t *s = vlc_object_create( p_input, sizeof( stream_t ) );
input_stream_sys_t *p_sys;
if( s )
{
s->p_sys = malloc( sizeof( input_stream_sys_t ) );
p_sys = (input_stream_sys_t*)s->p_sys;
p_sys->p_input = p_input;
}
return s;//注解:s->p_sys改变了
}

第二部分 程序框架实现
1. 播放列表文件src/playlist/playlist.c的线程
playlist_t * __playlist_Create ( vlc_object_t *p_parent )函数中创建的线程,线程函数为
static void RunThread ( playlist_t *p_playlist )
线程思路分析:
在RunThread里面执行循环,如果没有任务执行,则适当的延迟,如果接到p_playlist->i_status != PLAYLIST_STOPPED的条件,则调用PlayItem( p_playlist )函数,在PlayItem( p_playlist )函数中从新创建输入线程。

通过void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command,int i_arg )接收来自GUI界面的各种命令,然后设置p_playlist->i_status的状态,由该状态改变该播放列表文件主循环线程的执行。

2. 输入文件SRC/INPUT/INPUT.C的输入线程
input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
input_item_t *p_item )函数中创建的线程,线程函数为
static int RunThread( input_thread_t *p_input )
线程思路分析:
由 input_thread_t结构的成员分析是接收文件流还是网络流,如果是文件流,则调用file module 的读函数(pf_read)和打开函数(--).如果是network 则打开network module 的打开函数和读函数(pf_read)。
在 RunThread线程函数中接收数据和调用demux 或者decode etc处理。
一旦产生新的输入,则在播放列表线程中会首先结束该输入线程,然后从新创建新的输入线程。

3. 视频输出文件src/video_output/ video_output.c的线程
vout_thread_t * __vout_Create( vlc_object_t *p_parent,
unsigned int i_width, unsigned int i_height,
vlc_fourcc_t i_chroma, unsigned int i_aspect )函数中创建的线程,线程函数为
static void RunThread( vout_thread_t *p_vout)
线程思路分析:
在RunThread里面执行循环,任务是显示视频。

4. 在modules/gui/wxwindows/wxwindows.cpp中的GUI线程
static void Run( intf_thread_t *p_intf ) 函数中创建的线程,线程函数为
static void Init( intf_thread_t *p_intf )

线程思路分析:
在Init( intf_thread_t *p_intf )里面执行循环,创建新的GUI实例。Instance-》OnInit()(CreateDialogsProvider)-》DialogsProvider为运行的对话框。

接收网络文件的步骤
OnOpenNet( wxCommandEvent& event )打开网络文件的步骤。打开OpenDialog对话框,点击Ok后调用OpenDialog::OnOk( wxCommandEvent& WXUNUSED(event) )函数,调用playlist_Command函数改变播放列表线程的状态。

激活线程分析:
在wxwindow.cpp中的消息映射中 set_callbacks( OpenDialogs, Close ); 则设置了module_t->pf_activate= OpenDialogs函数,
在module.c 的__module_Need( vlc_object_t *p_this, const char *psz_capability,
const char *psz_name, vlc_bool_t b_strict )
函数中用到了pf_activate激活GUI对话框;
在video_output.c 的static void RunThread( vout_thread_t *p_vout)线程中,也用到了pf_activate激活GUI对话框;


5. 开始所有module 的精髓
消息映射宏
vlc_module_begin();
set_callbacks( NetOpen, NULL );
vlc_module_end();
然后设置模块结构的成员函数为:
#define set_callbacks( activate, deactivate ) /
p_submodule->pf_activate = activate; /
p_submodule->pf_deactivate = deactivate

在__module_Need函数中启动pf_activate 激活相应的module。

 

 

对VLC源代码阅读的计划是从其程序的框架开始,先对其主要的文件进行整理:
1.include/main.h 文件: access to all program variables,主要定义了2个结构体:libvlc_t,vlc_t。
         a. struct libvlc_t 根据程序注释:该结构体只有一个实例,在main函数中被分配,而且只能在main中访问。它用来存储一些只能初始化一次的数据,比如说cpu容量或者global lock.
          b. struct vlc_t   注释称:This structure is a LibVLC instance
         libvlc_t,vlc_t在VLC_COMMON_MEMBERS宏中出现,分别定义了 libvlc_t *   p_libvlc; vlc_t *   p_vlc; 对象,注释称为 root of the evil,可见其结构体的重要性.所有的参数传递都在这里面(具体尚不清楚)。
2.include/Vlc_common.h 文件:common definitions,Collection of useful common types and macros definitions,通用类型和宏定义的集合
         主要作用是为了将不同的操作系统中的变量定义统一起来,比如说根据将unit_8来统一代表unsiged char类型.
        该文件中还定义了VLC_COMMON_MEMBERS宏,该宏中包括了所有VLC基本对象的通用成员变量:these members are common for all vlc objects。
        定义导出函数
#ifndef __PLUGIN__
#   define VLC_EXPORT( type, name, args ) type name args
#else
#   define VLC_EXPORT( type, name, args ) struct _u_n_u_s_e_d_
extern module_symbols_t* p_symbols;
#endif
定义回调函数
      typedef int ( * vlc_callback_t ) ( vlc_object_t *,      /* variable's object */
char const *,            /* variable name */
vlc_value_t,             /* old value */
vlc_value_t,            /* new value */
                                                  void * );                 /* callback data */          
3.include/vlc_objects.h 文件:vlc_object_t definition and manipulation methods,vlc_object_t的定义和处理函数
       struct vlc_object_t
{
VLC_COMMON_MEMBERS
}; //定义一个结构来使用宏定义的公共成员

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/455427.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flask 概述

什么是Web Framework? Web Application Framework(Web应用程序框架)或简单的Web Framework(Web框架)表示一个库和模块的集合,使Web应用程序开发人员能够编写应用程序,而不必担心协议&#xff0…

(五)Maven中的聚合和继承

一、为什么要聚合? 定义:我们在开发过程中,创建了2个以上的模块,每个模块都是一个独立的maven project,在开始的时候我们可以独立的编译和测试运行每个模块,但是随着项目的不断变大和复杂化,我们…

python堆栈反向输出列表_python - IPython:将Python脚本的输出重定向到文件(如bash) - 堆栈内存溢出...

IPython有自己的上下文管理器来捕获stdout / err ,但它没有重定向到文件,它重定向到一个对象:from IPython.utils import iowith io.capture_output() as captured:%run my_script.pyprint captured.stdout # prints stdout from your script…

关于datagrid

基本在公司使用的datagrid不需要自己写前台代码,只需要自己给grid明确id,url以及列属性即可。 后台需要返回一个数据类型:{recordsFiltered2, data[], drawnull, recordsTotal2},通常返回这个数据类型的话,只需要调用d…

M-JPEG、MPEG4、H.264都有何区别 依维安防论坛

压缩方式是网络视频服务器和网络摄像机的核心技术,压缩方式很大程度上决定着图像的质量、压缩比、传输效率、传输速度等性能,它是评价网络视频服务器和网络摄像机性能优劣的重要一环。 随着多媒体技术的发展,相继推出了许多压缩编码标准&…

Django/Flask/Tornado三大web框架性能分析

写在前面:本文的数据涉及到之前遇到过的问题,大概一次 http 请求到收到响应需要多少时间。这个问题在实际工作中与框架有比较大的关系,因此特别就框架的性能做了一次分析。这里使用之前的一个报告数据: Pythons Web Framework Ben…

python urllib模块学习笔记

这个模块是最基本最常用的,以前看过,总结一下 #coding : utf-8import urlliburl http://cnblogs.com#代理服务器proxies {http:http://127.0.0.1:8087}#使用代理服务器打开r urllib.urlopen(url,proxies proxies)print r.info()print r.getcode()pri…

hibernate基础工具findBySQL学习

public List<Map<String,Object>> findBySQL(String sql,Map<String,Object> param,int start,int max) {log.debug("finding List by hql");try {       //最后返回map map的key可为别名和数据库字段SQLQuery querysessionFactory.getCurr…

python处理ini文件_python对ini配置文件处理

>>> cf.read("test.ini") #读取配置文件[test.ini]>>> cf.sections() #片段名[base, callback]>>> cf.options("callback") #配置…

Python实现自动推本地github博客到远程仓库

Python实现自动推本地github博客到远程仓库 以前的简单版本 通过python中的os模块操作系统命令 详情可参考:Python实现一行代码推本地git到远程仓库 升级版本 本次加入了监听文件修改功能 这样脚本只需在后台运行,即可检测到对应的文件夹中的内容是否变化 如果变化,则调用…

H.264/MPEG-4 AVC

维基百科&#xff0c;自由的百科全书跳转到&#xff1a; 导航, 搜索 跳过字词转换说明 汉漢▼▲为了阅读方便&#xff0c;本文使用全文手工轉換。转换内容&#xff1a;本文采用电脑和信息技术组全文转换 [查看] • [编辑] • [强制刷新] 以下為本條目單獨的全文轉換&#xff0c…

JavaScript 专题之函数柯里化

JavaScript 专题系列第十三篇&#xff0c;讲解函数柯里化以及如何实现一个 curry 函数 定义 维基百科中对柯里化 (Currying) 的定义为&#xff1a; In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes m…

机器学习模板

根据心情补充&#xff0c;语言都是Python hash&#xff0c;把所有的文本转化成数字 from sklearn.preprocessing import LabelEncoder for c in train.columns:if train[c].dtype object:lbl LabelEncoder()lbl.fit(list(train[c].values) list(test[c].values))train[c] l…

漂亮特殊字体可复制_12个创意字体免费下载网站

今天为大家介绍12个创意字体的网站&#xff0c;这些网站都有提供免费下载的字体哦&#xff0c;希望对大家在创作上面有所帮助。FontSpace在Fontspace上有超过42000种免费字体。在这里字体被整齐的分门归类&#xff0c;帮助你找到想要的字体。除了典型的“serif” “script”等&…

使用postman测试接口

Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。在java web开发中使用非常多&#xff0c;经常用来测试接口。 使用postman模拟json数据的发送 第一步:在header里边设置发送数据的类型 Paste_Image.png设置发送数据类型为json&#xff0c;也就是key为Content-…

删除github上的commit历史记录

删除github上的commit历史记录 起步 今天小编发现了git克隆下来的远程库特别大: 经过查询之后发现是每次推送之后都会留下记录缓存&#xff0c;这样很多没用的记录就会占用多余的空间&#xff0c;别人克隆的时候也会多耗费时间&#xff0c;今天我查到了一个清除无用记录的方…

DirectShow组件原理分析及应用

1 DirectX简介  DirectX是Microsoft公司为游戏和其他高性能多媒体应用所提供的一套底层应用程序编程接口。这些接口包括对二维和三维图形&#xff0c;声效和音乐&#xff0c;输入设备以及多玩家网络游戏等的支持。目前DirectX的最高版本是DirectX 9.0。  1.1 DirectX的组成…

接口安全

老大发了篇文章&#xff0c;让看如何写出安全的接口。 如何写出安全的API接口&#xff1f;接口参数加密签名设计思路转载于:https://www.cnblogs.com/Tpf386/p/7053795.html

python中xml模块_python学习第十五天-2(XML模块)

也是一种文本转换形式。importxxxxxxxxxxxxxxxxxxx asxx,可以用xx代替xxxxxxxxxxxxxxxxxxx模块xml文件的新增&#xff0c;修改&#xff0c;删除&#xff0c;查询。新增&#xff1a;​import xml.etree.ElementTreeas ET​new_xmlET.Element(nameList)#创建xml的根节点相当于na…

ubuntu系统下Java环境JDK的安装

Debian Linux下安装jdk 下载压缩包 官网下载对应的.gz包 点击下载 解压文件 创建一个目录用于存放解压后的文件&#xff0c;并解压缩到该目录下 sudo mkdir /opt/java8 sudo tar -zxvf jdk-8u221-linux-x64.tar.gz -C /opt/java8修改环境变量 sudo vim ~/.bashrc 进入…