? c++ ? ? ffmpeg ? ? 播放器 ? ? SDL ?    发布于 2019-01-31   1337人围观   0条评论

前言

本文参考雷霄骅的博文https://blog.csdn.net/leixiaohua1020/article/details/8652605,使用c++根据ffmpeg-4.1版本改写(原文代码基于旧版本ffmpeg)。代码见下文。

本文代码地址见https://github.com/2997215859/ffplay-learn/blob/master/Video/print_info.cpp

本文代码基于ffmpeg-4.1版本,事先需要安装好ffmpeg

本文代码提供CMakeLists.txt,见附录CMakeLists.txt部分,或根据CMakeLists.txt改写。需要链接的库如下(基本上安装ffmpeg、ffplay、SDL2之后就有了)。

 1   avdevice avfilter avformat avcodec swscale swresample postproc avutil m xcb xcb-shm xcb xcb-shape xcb xcb-xfixes xcb-render xcb-shape xcb asound pthread m fontconfig freetype freetype z bz2 lzma SDL2 SDL2main

 流程

 

解码器即C++中使用ffmpeg解码视频到YUV数据示例,SDL渲染是一个封装了音视频底层接口的库,本文使用2.0版本。

解码器主要使用ffmpeg中的几个函数:avformat_alloc_context, avcodec_find_decoder, avcodec_send_packet​, avcodec_receive_frame

代码剖析

读取视频格式并获取视频流的索引

    string filepath = "/home/sensetime/videos/big_buck_bunny_720p_30mb.mp4";

    avdevice_register_all();
    avformat_network_init();

    AVFormatContext *avFormatContext = avformat_alloc_context();
    if (avformat_open_input(&avFormatContext, filepath.c_str
查看更多
? c++ ? ? ffmpeg ? ? hello ?    发布于 2019-01-23   1953人围观   0条评论

前言

本文参考雷霄骅的博文最简单的基于FFMPEG的Helloworld程序,使用c++根据ffmpeg-4.1版本改写(原文代码基于旧版本ffmpeg)。代码见下文。

本文代码地址见https://github.com/2997215859/ffplay-learn/blob/master/Video/print_info.cpp

本文代码基于ffmpeg-4.1版本,事先需要安装好ffmpeg

本文代码提供CMakeLists.txt,见附录CMakeLists.txt部分,或根据CMakeLists.txt改写。需要链接的库如下(基本上安装ffmpeg、ffplay、SDL2之后就有了)。

avdevice avfilter avformat avcodec swscale swresample postproc avutil m xcb xcb-shm xcb xcb-shape xcb xcb-xfixes xcb-render xcb-shape xcb asound pthread m fontconfig freetype freetype z bz2 lzma SDL2 SDL2main

 

代码

主函数

int main () {

    cout << "\n<<Configuration>>\n" << configurationInfo(); // 打印ffmpeg的configure信息

    cout << "\n<<URLProtocol>>\n" << urlProtocolInfo(); // 打印URL

    cout << "\n<AVFormat>\n" << formationInfo();

    cout << "\n<<AVCodec>>\n" << avcodecInfo();

    cout << "\n<<AVFilter>>\n" << avfilterInfo();

    return 0;
}

 

分函数

1. 打印ffempg的configure信息,直接调用avdevice_configuration函数,返回char字符串

string configurationInfo () {
    return string(avdevice_configuration());
}

2. 利用avio_enum_p

查看更多
? linux ? ? 服务端 ? ? 多线程 ? ? c++ ?    发布于 2018-12-30   822人围观   0条评论

前言

最近在读陈硕的moduo网络库的书,记录总结一些东西。

本章内容主要介绍muduo网络库的简单使用和muduo多线程模型,以及比较muduo网络库和其他一些网络库的性能。muduo网络库的使用在此不赘述,和大部分网络库使用差不多。本章内容中让我收获比较大的是,6.6节在详解muduo多线程模型时,比较了常见的并发网络服务程序设计方案。读完这章的我有一个巨大的疑问,就是到底应该如何有效的提高并发连接数?这个问题暂时还没想的太明白

常见的并发网络服务程序设计方案

陈硕在附录A中举了三大TCP网络编程案例:echo服务器、chat聊天/聊天室以及proxy代理服务器。

方案0 单线程 accpet + read/write 最最基本的模型 

方案1 accept + fork 即每个连接均以一个进程来处理 process-per-connection

方案2 accpet + thread 即每个连接均以一个线程来处理 thread-per-connection

方案3 pre  fork 

方案4 pre threaded

方案5 单线程reactor 即单线程poll然后read/write

方案6 (过渡方案)单线程reactor + thread-per-task 依旧是单线程poll,不同的是,每个请求(不是每个链接)创建一个线程

方案7 (过渡方案)单线程reactor + work thread 依旧是单线程poll,不同的是,每个连接创建一个线程,相同链接上的请求由同一个计算线程处理,以此保证同一个连接上请求结果的顺序性,而方案6无法保证这一点。该方案的问题在于,可能还不如直接用方案2了呢。因为该方案并发连接数限制于线程数目。

方案8 单线程reactor + thread-pool 基于方案6,构造线程池,Reactor线程处理IO,计算任务交个线程。每个请求分发给计算线程池处理。该方案的每个连接上的一长串请求有乱序返回的可能,所以需要依靠id来梳理响应

方案9 reactor in threads,即one loop per thread,一个main Reactor 负责accept(2)连接,然后将连接分发到其他线程的sub Reactor上。

方案10 reactor in process,nginx内置方案。和方案9大同小异

方案11 多reactors + threa

查看更多
? linux ? ? 服务端 ? ? 多线程 ? ? c++ ?    发布于 2018-12-14   250人围观   0条评论

前言

最近在读陈硕的moduo网络库的书,记录总结一些东西。

本章分析和设计一个高效的多线程日志库。

功能需求

  • 必要的需求
    • 日志级别设定
  • 非必要需求
    • 日志目的地 - 对于诊断日志来说,日志目的地即本机,因为往网络写日志消息并不可靠,诊断日志的功能之一就是诊断网络故障。
    • 日志消息格式可配置 - 可变可不变,尽量保持日志解析的方便程度,即尽量保持日志格式的不可变性
    • 日志运行时过滤器 - 控制不同组件输出不同的日志级别,可要可不要

性能需求

简而言之,就是高效,日志前端写入不会阻塞或尽量不阻塞。对日志消息前端而言,要做到低延迟、低CPU开销、少阻塞;对于日志消息后端而言,要做到足够大的吞吐量以及占用较少资源。

书中列出了几个具体指标:

  • 每秒写几千上万条日志消息时,不会带来明显的性能损失
  • 能应对一个进程产生大量日志数据的场景,例如1GB/min
  • 不阻塞正常执行的流程
  • 在多线程程序中,不造成争用

多线程异步日志

在多线程服务程序中,如果在网络IO或者业务线程中直接往磁盘写数据,写操作可能会比较耗时,直接导致请求方超时。故,在常规的实时业务处理流程中应该避免磁盘IO。

以下过程仅仅是自述理解使用,具体过程建议看书,书中写的非常详细。

muduo网络使用的双缓冲技术,即准备两块buffer,A和B,然后日志前端会兵乓式往A和B写入日志消息,日志后端会处理不在使用的那块buffer中的日志消息。在实施过程中准备了四块buffer,日志前端持有A和B,日志后端持有C和D。日志前端和后端分别持有两块Buffer Vector,V1和V2。

日志前端往A中写数据,写满时将A压入V1,用B替换A,之后继续往B中写入数据,通知日志后端处理数据。

日志后端处理过程为,等待日志前端写满A的通知,压入当前正使用的buffer到V1中,并替换日志前端的A和B为日志后端的C和D。日志后端交换V1和V2,该操作即清空日志前端的Buffer Vector,并将原Vector交于后端处理。日志后端在写入日志数据到磁盘之后,重新用A和B填满后端所需要的两块buffer。

查看更多
? linux ? ? c++ ? ? 多线程 ?    发布于 2018-12-08   571人围观   0条评论

前言

最近在读陈硕的moduo网络库的书,记录总结一些东西。

这章内容比较散,主要介绍多线程系统编程需要理解的一些知识点。看完的整个感受就是可以直接看最后的总结就行了,中间有些内容不是非常能理解的透彻。

基本线程原语

基本线程原语只需要三样东西:thread、mutex、condition。

  1. thread的创建与销毁
  2. mutex的创建、销毁、加锁、解锁
  3. condition_variable的创建、销毁、通知、广播、等待

有了这三样的东西,可以完成任何多线程编程任务。通常,不会直接使用thread和condition_variable,取而代之的是,高级的编程构建,比如线程池,比如之前提到过的CountDownLatch用于主线程和其他线程之间的同步。

C++系统库的线程安全性

这一节中的内容有些东西我还没能够完全理解,现下只记录下一些可理解的内容。以后回过头来再看看。

C++的标准库容器和std::string都不是线程安全的,只有std::allocator保证是线程安全的。一方面的原因是为了避免不必要的性能开销,另一方面的原因是单个成员函数的线程安全并不具备可组合性。

C++标准库中的绝大多数泛型算法是线程安全的,因为这些都是无状态纯函数。只要输入区间是线程安全的,那么泛型算法就是线程安全的。

 

Linux上的线程标识

linux上的线程标识不适合使用pthread_t,原因很多,详情见书。取而代之,使用的是gettid(2)系统调用,其优势书中也写的很清楚。

为了避免效率问题,使用__thread关键字做了缓存,避免每次获取线程id时都需要执行一次系统调用。

线程的创建与销毁的守则

线程的创建

线程的创建需要遵循以下几个原则:

  1. 程序库不应该在未提前告知的情况下创建自己的背景线程
  2. 尽量用相同的方式创建线程
  3. 在进入main()函数之前不应该启动线程
  4. 程序中线程的创建最好能在初始化阶段全部完成,不要为了每个计算任务或者每个网络连接去实时创建线程

以上四点都比较直观,也很容器理解。

针对第一点,如果程序库需要使用背景线程,那么最好让使用者在初始化库时传入线程池或者event loop对象,这样做是为了方便统筹线程的数目和用途,避免低优先级程序的任务独占某个线程。如果程序库在未告知的情况下使用了额外线程,那么会使得我们在规划线程资源的时候漏算一部分,甚至可能使得关键任务的计算资源无法达到性能指标。

针对第二点,统一的方

查看更多