从FFmpeg 4. 2源码中提取dshow mjpeg code步骤
之前在https://blog.csdn.net/fengbingchun/article/details/103735560 中介绍过在Windows上通过vs2017编译FFmpeg源码进行单步调试的步骤,为了进一步熟悉FFmpeg这里以提取FFmpeg dshow mjpeg源码为例介绍其实现过程及注意事项:
FFmpeg是用C实现的,为了加速,个别模块也有对应的汇编实现。之前在https://blog.csdn.net/fengbingchun/article/details/102641967中介绍过从OpenCV中提取dshow mjpeg的步骤,但是OpenCV中只能拿到解码后的数据不能拿到解码前即编码的数据,而FFmpeg可以获取到编码数据。
这里仅提取与获取dshow mjpeg编码数据仅包括视频不包括音频相关的code,涉及到的对外C接口包括11个:avdevice_register_all、avformat_alloc_context、av_find_input_format、av_dict_set、avformat_open_input、av_malloc、av_read_frame、av_packet_unref、av_freep、avformat_close_input、av_dict_free。主要代码实现在libavdevice模块,这里按照测试code的调用顺序依次说明:
1. avdevice_register_all:初始化avdevice模块,并注册所有输入输出设备。
因为我们只是获取dshow mjpeg的编码数据,不会用到AVOutputFormat相关内容,因此对于outdev_list数组不需要提取ff_opengl_muxer和ff_sdl2_muxer的实现,仅需令outdev_list[]={NULL};即可。
对于indev_list数组仅需要提取ff_dshow_demuxer的code,因此仅需令indev_list[]={&ff_dshow_demuxer, NULL};即可。ff_dshow_demuxer的实现在libavdevice模块的前缀为dshow的那些文件中,那些文件基本上都需要提取出来。
在av_format_init_next函数中用到的数组变量muxer_list和demuxer_list全部赋值为NULL即可。好像函数av_format_init_next什么都没做,也可直接去掉此函数。
2. avformat_alloc_context:分配AVFormatContext。对AVFormatContext执行malloc、memset(0)操作,以及一些默认设置。其中内部的回调函数io_open和io_close无需提取。并显式将编解码格式设置为mjpeg。
3. av_find_input_format:根据输入格式的名字即”dshow”查找AVInputFormat。其实就是将ff_dshow_demuxer赋值给了外部的AVInputFormat指针对象。
4. av_dict_set:设置一个AVDictionary项。这里主要是设置video size。其实就是将key即“video_size”和value即”1280x720”赋值给AVDictionary。
5. avformat_open_input:打开输入流并读取头信息。之前在上面第2步中对AVFormatContext作了memset全赋值为0操作,因此avformat_open_input函数中一些if判断始终会为false如pb,这些code可注释掉。这个函数里面最重要的是AVInputFormat中的回调函数read_header,它会开启摄像头。
6. av_malloc:为一个AVPacket分配内存块。以64位对齐方式调用系统_aligned_malloc函数。
7. av_read_frame:获取帧数据,每调用一次获取一帧mjpeg编码数据。这里的主要实现函数是read_frame_internal,调用完此函数后就会jump到return_packet处,其它部分code不会走到如for内code,因此这部分code可全注释掉。read_frame_internal实现有些复杂,不过里面的一些if判断会始终为false,可以注释掉那些code。
8. av_packet_unref:释放数据缓冲区并将AVPacket字段重置为默认值。每正常调用一次av_read_frame函数就应该对应调用一次av_packet_unref函数。
9. av_freep:释放申请的内存块,调用系统_aligned_free函数,如果AVPacket是有效的,在调用此函数前需要先调用av_packet_unref。
10. avformat_close_input:关闭打开的AVFormatContext并释放。
11. av_dict_free:释放为AVDictionary分配的内存,内部调用的是av_freep函数。
注意事项:
1. C语言语法与C++的差异:
(1).C中从void*到一个具体的结构体可以直接赋值,但C++需要使用static_cast。
(2).C中从int到enum可以直接赋值,但C++需要使用static_cast。
(3).C中对结构体赋值支持”.结构体成员变量名=value”,而且成员变量名的顺序可以任意,但C++需要完全按照变量名顺序依次直接给出各个变量名的value,不支持”.变量名=value”。
(4).C中会将class、this、new等当作普通变量名使用,但在C++中这些属于关键字,需要修改成其它名字。
(5).C中对union支持”.union成员变量名=value”,C++不支持,这里将union修改成了struct,如AVOption中的default_val。
2. 在移植dshow.c文件到c++文件时,发现需要define宏COBJMACROS和CINTERFACE,定义完这两个宏后,include文件ObjIdl.h和Shlwapi.h时总会有各种问题,好像定义那两个宏后,这两个include文件不能同时在同一个.cpp文件中,临时解决方法是将dshow.c拆分成几个.cpp文件。
3. 移植code中,有些分支或函数感觉执行不到,临时使用一个宏ERROR_POS占位,此宏定义如下,发现问题可快速定位:
#define ERROR_POS \fprintf(stderr, "Error, It should not execute to this position: file: %s, func: %s, line: %d\n", __FILE__, __FUNCTION__, __LINE__); \abort();
4. code中用到了一些系统接口,需要依赖两个系统库:strmiids.lib、shlwapi.lib。
5. 提取的全部code存放在:https://github.com/fengbingchun/OpenCV_Test/tree/master/src/fbc_cv
测试代码如下:将函数中的命名空间fbc去掉即可在原始ffmpeg中运行
#include <fstream>
#include <iostream>
#include "fbc_cv_funset.hpp"
#include <videocapture.hpp>
#include <opencv2/opencv.hpp>
#include <avdevice.hpp>
#include <avformat.hpp>
#include <avutil.hpp>
#include <avmem.hpp>int test_ffmpeg_dshow_mjpeg()
{
#ifdef _MSC_VERfbc::avdevice_register_all();fbc::AVFormatContext* format_context = fbc::avformat_alloc_context();fbc::AVCodecID id = fbc::AV_CODEC_ID_MJPEG;format_context->video_codec_id = id;fbc::AVInputFormat* input_format = fbc::av_find_input_format("dshow");if (!input_format) {fprintf(stderr, "Error: input format is not supported\n");return -1;}fbc::AVDictionary* dict = nullptr;int ret = fbc::av_dict_set(&dict, "video_size", "1280x720", 0);if (ret < 0) {fprintf(stderr, "Error: fail to av_dict_set: %d\n", ret);return -1;}ret = fbc::avformat_open_input(&format_context, "video=Integrated Webcam", input_format, &dict);if (ret != 0) {fprintf(stderr, "Error: fail to avformat_open_input: %d\n", ret);return -1;}int video_stream_index = -1;for (unsigned int i = 0; i < format_context->nb_streams; ++i) {const fbc::AVStream* stream = format_context->streams[i];if (stream->codecpar->codec_type == fbc::AVMEDIA_TYPE_VIDEO) {video_stream_index = i;fprintf(stdout, "type of the encoded data: %d, dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",stream->codecpar->codec_id, stream->codecpar->width, stream->codecpar->height, stream->codecpar->format);//break;}}if (video_stream_index == -1) {fprintf(stderr, "Error: no video stream\n");return -1;}fbc::AVCodecParameters* codecpar = format_context->streams[video_stream_index]->codecpar;if (codecpar->codec_id != id) {fprintf(stderr, "Error: this test code only support mjpeg encode: %d\n", codecpar->codec_id);return -1;}fbc::AVPacket* packet = (fbc::AVPacket*)fbc::av_malloc(sizeof(fbc::AVPacket));if (!packet) {fprintf(stderr, "Error: fail to alloc\n");return -1;}std::ofstream out("E:/GitCode/OpenCV_Test/test_images/test.mjpeg", std::ios::binary | std::ios::out);if (!out.is_open()) {fprintf(stderr, "Error, fail to open file\n");return -1;}int count = 0;while (count++ < 100) {ret = fbc::av_read_frame(format_context, packet);if (ret >= 0 && packet->stream_index == video_stream_index && packet->size > 0) {fprintf(stdout, "packet size: %d\n", packet->size);out.write((char*)packet->data, packet->size);}else if (ret < 0 || packet->size <= 0) {fprintf(stderr, "Warnint: fail to av_read_frame: %d, packet size: %d\n", ret, packet->size);continue;}fbc::av_packet_unref(packet);}fbc::av_freep(packet);fbc::avformat_close_input(&format_context);fbc::av_dict_free(&dict);out.close();fprintf(stdout, "test finish\n");return 0;
#elsefprintf(stderr, "Error: only support windows platform\n");return -1;
#endif
}
生成的test.mjpeg文件可使用ffplay直接播放,执行结果如下:
GitHub:https://github.com/fengbingchun/OpenCV_Test
相关文章:

ControlButton按钮事件
#ifndef __HControlButton_H__#define __HControlButton_H__#include "cocos2d.h"#include "cocos-ext.h"USING_NS_CC;USING_NS_CC_EXT; //用于标识当前按钮的状态typedef enum{ touch_begin, touch_down, touch_up,}tagForTouch;class HControlB…

swift3.0UIAlertController使用方法
let alertVC : UIAlertController UIAlertController.init(title: "添加照片", message: "", preferredStyle: .actionSheet) let cleanAction UIAlertAction(title: "取消", style: UIAlertActionStyle.cancel,handler:nil) let photoActi…

Doxygen使用介绍
Doxygen的主页为http://doxygen.nl/,它的license为GPL,最新发布版本为1.8.17,源代码存放在https://github.com/doxygen/doxygen,它支持的语言包括C、C、Objective-C、C#、Java、Python等,它支持的系统平台包括Winodws、…
云计算软件生态圈:摸到一把大牌
作者 | 老姜编辑 | 阿秃出品 | CSDN云计算(ID:CSDNcloud)“我觉得我摸着了一把大牌。”软件领域的新锐企业——有赞公司创始人兼CEO白鸦在转向SaaS领域的一个细分市场时,曾对天使投资人这样说。而老牌软件企业金蝶创始人徐少春在2…

iOS封装HTTPS双向和单向验证
1.HttpsUtil (1) 对双向和单向验证的封装 #import <Foundation/Foundation.h> #import "AFNetworking.h" interface HttpsUtil : NSObject // 双向认证 (void)configHTTPSessionManager:(AFHTTPSessionManager *)manager serverCers:(NSArray *) serverCerNam…

开源库BearSSL介绍及使用
BearSSL是用C语言实现的SSL/TLS协议,它的源码可直接通过git clone https://www.bearssl.org/git/BearSSL 下载,它的license是MIT,最新版本为0.6。 BearSSL的主要特性是: (1). 正确且安全:对不安全的协议版本和算法选…
个推CTO安森:我所理解的数据中台
作者 | 个推CTO安森来源 | 个推技术学院(ID:ID: getuitech)引言在前面两篇文章(《数据智能时代来临:本质及技术体系要求》和《多维度分析系统的选型方法》)之中,我们概括性地阐述了对于数据智能的理解&…

玩弹珠手游-杂想
前言 为什么会写这个杂想呢? 因为最近这一个月来,我有点太沉迷怪物弹珠这个游戏了,每天下班回来的时间和上下班路途都在玩这个游戏,占据了我大部分的业余时间,也该是时候放一放玩游戏了。 为什么会玩这个游戏呢&#x…

OC封装时间选择器
#import <UIKit/UIKit.h> protocol TimeDatePickerViewDelegate <NSObject> //必须实现的两个协议 required - (void)changeTime : (NSDate *)date;//当时改变时出发 - (void)daterMine : (NSDate *)date;//更确定时间 end interface TimeDatePickerView :UIView /…
银行卡大小的充电宝,买就送耳机!
每个人的朋友圈和微博上似乎都有那么几个活得让人羡慕的朋友他们的生活看起来不仅精致,还很丰富多彩从早上第一刻就开始了↓出门旅游新一天的穿搭逆天朋友团咖啡馆到书店逼格十足的日料夜景太迷人忍不住发个小视频最后一定不要错过傍晚的夕阳,真的好上镜…

C++中插件使用举例
插件并不是在构建时链接的,而是在运行时发现并加载的。因此,用户可以利用你定义好的插件API来编写自己的插件。这样他们就能以指定方式扩展API的功能。插件库是一个动态库,它可以独立于核心API编译,在运行时根据需要显示加载。不过…

C和C++安全编码笔记:指针诡计
指针诡计(pointer subterfuge)是通过修改指针值来利用程序漏洞的方法的统称。 可以通过覆盖函数指针将程序的控制权转移到攻击者提供的外壳代码(shellcode)。当程序通过函数指针执行一个函数调用时,攻击者提供的代码将会取代原本希望执行的代码而得到执行。 对象指…
runLoop和runtime的分析
一.RunLoop: Runloop是事件接收和分发机制的一个实现。 Runloop提供了一种异步执行代码的机制,不能并行执行任务。 在主队列中,Main RunLoop直接配合任务的执行,负责处理UI事件、定时器以及其他内核相关事件。 (1).RunLoop的主要目的&#…
脑出血遇到深度学习,是否可以无所遁形?
近期大家对身体健康这个话题格外关注,而我们今天公开课的主题也恰巧与此不谋而合。我国脑卒的发病率已经超过心血管疾病,成为致死、致残率最高的疾病,并且发病率呈逐年上升的趋势,此外脑血管病和颅内肿瘤等脑部疾病也危害人们的健…

Cloudera Manager 5.3 和 CDH5.3.0 本地(离线)
为什么80%的码农都做不了架构师?>>> 声明一下:http://my.oschina.net/dataRunner/blog/369129 是本人所写,并非抄袭。 有部分内容来自 http://www.wangyongkui.com/hadoop-cdh5/ 这个文件是根据官网操作,翻译的不…
万字长文详解如何用Python玩转OpenGL | CSDN 博文精选
作者 | 天元浪子来源 | CSDN博文精选【编者按】OpenGL(开放式图形库),用于渲染 2D、3D 矢量图形的跨语言、跨平台的应用程序编程接口,C、C、Python、Java等语言都能支持 OpenGL。本文作者以 Python 语法为例,用两万字详…

模仿视频抓帧实现
路口或某些场所可能并不会把从摄像头获取到的视频全部存储下来或对所有的视频帧进行处理,即摄像设备是一直处于打开状态,可能会根据需要间隔性的抓取其中一帧,或当某事件触发时才会抓取当前的一帧数据进行处理。这里使用两个线程来模仿此场景…

iOS--MD5加密封装
#import <Foundation/Foundation.h> interface MD5 : NSObject /** * md5加密 * * param inPutText 需要加密的字符串 * * return 加密好的字符串 */ (NSString *)md5:(NSString *)inPutText; end #import "MD5.h" #import "CommonCrypto/CommonDiges…

Akka路由_RoundRobinRoutingLogic
2019独角兽企业重金招聘Python工程师标准>>> Akka路由_RoundRobinRoutingLogic 使用Round Robin算法的Router,代码中有注释,基本和上篇文章中的代码一样 http://my.oschina.net/xinxingegeya/blog/369721, 具体如下,关…

iOS ---网络请求封装(自动缓存与手动缓存)
#import <Foundation/Foundation.h> interface WNetworkCache : NSObject /** * 手动写入/更新缓存 * * param jsonResponse 要写入的数据 * param URL 请求URL * * return 是否写入成功 */ (BOOL)saveJsonResponseToCacheFile:(id)jsonResponse andURL:(NSStrin…

Windows下获取视频设备的一种改进实现
之前在https://blog.csdn.net/fengbingchun/article/details/102806822中介绍过在Windows下获取视频设备列表的方法。其实那种实现方法是有缺陷的,当PC机上连接多个视频设备,并且其中有设备处于启动运行状态时,再调用相关接口获取视频设备可能…
最新单步目标检测框架,引入双向网络,精度和速度均达到不错效果
作者 | Tiancai Wang等译者 | 路一直都在出品 | AI科技大本营(ID:rgznai100)one-stage的目标检测方法因其具有实时性强、检测精度高等特点,近年来受到广泛关注。目标检测包括分类和定位两个子任务,通常来说,one-stage目…

基于Sentinel的Redis3.2高可用方案
默认情况下,Redis node和sentinel的protected-mode都是yes,在搭建集群时,若想从远程连接redis集群,需要将redis.conf和sentinel.conf的protected-mode修改为no,若只修改redis node,从远程连接sentinel后&am…
从YARN迁移到k8s,滴滴机器学习平台二次开发是这样做的
整理 | 夕颜出品 | AI科技大本营(ID:rgznai100)【导读】人工智能时代,机器学习已经渗透进每个领域,改变了这些领域的业务模式、技术架构以及方法论。随着深度学习技术近年来快速发展,高效、易用的机器学习平台对于互联…

最新 macOS Sierra 10.12.3 安装CocoaPods及使用详解
cocoapods官网:https://guides.cocoapods.org 一、什么是CocoaPods 每种语言发展到一个阶段,就会出现相应的依赖管理工具,例如 Java 语言的 Maven,nodejs 的 npm。随着 iOS 开发者的增多,业界也出现了为 iOS 程序提供…

libusb中的热插拔使用举例
以下为判断usb设备是插入还是拔出状态(热插拔)的测试代码: 在Windows下是不支持的,在Linux是支持的,下一个版本可能会支持Windows下的热插拔: #include <chrono> #include <thread> #include <iostream> #incl…

C++复制控制:拷贝构造函数
一、拷贝构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。与默认构造函数一样 ,拷贝构造函数可由编译器隐式调用。拷贝构造函数应用的场合为: (1࿰…

关于IOS获取本地通讯录信息(包含iOS9.0前后)
在ios开发当中,获取用户本地的通讯录功能愈加频繁的出现,七两自己也在自己公司的项目当中遇到的获取本地的通讯录信息的功能(俗称“种子用户功能”,太可怕了)。对此七两总结了自己使用本地通讯录时的注意点,…

C和C++安全编码笔记:动态内存管理
4.1 C内存管理: C标准内存管理函数: (1).malloc(size_t size):分配size个字节,并返回一个指向分配的内存的指针。分配的内存未被初始化为一个已知值。 (2).aligned_alloc(size_t alignment, size_t size):为一个对象…