背景
为了实现无人机视频实时推流和图像处理,首先要完成视频编解码,大疆的视频接口实在是坑太多了!参考了很多大神的文章,大多都是解码本地文件或者直接从服务器拉流,不能实现我想要的实时动态流解码,搞了半个月终于能实时解码了,希望我的研究结果能帮助更多人。
- 主流视频压缩格式是h264(IDR编码),相关教程很多,而GDR编码相关的内容几乎没有,没法以字节流提取nalu的方式解码;
- 直接采样飞机视频保存为本地文件无法直接播放,转为mp4格式后播放会有花屏;
- 大疆的软解接口cpu负载太高。
大疆M300的H20主摄像头视频编码格式为h264(GDR),OSDK-4.0提供了三个飞机摄像头视频接口sample。
- OSDK调用ffmpeg软解h264裸流,传出OpenCV可读的Mat数组,直接显示一帧图像;
- 以第一个接口为基础,增加了循环和睡眠,连续推出图像帧模拟视频流;
- 直接提供一个h264(GDR)裸流的函数,传出参数是一个数据包地址和数据包长度。
问题
有一个问题,大疆提供的接口初始化摄像头相关参数后会循环回调一个数据包推送的liveView函数,而GStreamer导入外部数据的插件appsrc也需要循环回调need-data函数,两个循环回调函数同时运行还要数据交互同步,我设计了一个循环队列的缓冲区来存放公共数据,使用互斥锁配合条件变量来保证两个线程同步。
实现
发送端:
/***************************************
@Copyright(C) All rights reserved.
@Author DENG Ganglin
@Date 2020.08
***************************************/
#include "dji_vehicle.hpp"
#include "dji_linux_helpers.hpp"
#include <gst/gst.h>
#include <gst/gstminiobject.h>
#include <glib.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include "loopqueue.hpp"
using namespace DJI::OSDK;
using namespace std;
// 创建插件
GstElement *pipeline, *appsrc, *parse, *decoder, *scale, *filter1, *conv, *encoder, *filter2, *rtppay, *udpsink;
GstCaps *scale_caps;
GstCaps *omx_caps;
static GMainLoop *loop;
GstBuffer *buf;
// 定义结构体,保存生产者的数据
typedef struct Msg
{
int len;
uint8_t* data;
}msg;
// 创建循环队列
LoopQueue<msg*> loopqueue(256);
pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER; // 初始化互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 初始化条件变量
int count_t = 0; // 打印检查数据流动是否正常
static void cb_need_data(GstElement *appsrc, guint size, gpointer user_data)
{
static GstClockTime timestamp = 0;
GstFlowReturn ret;
GstMapInfo map;
count_t++;
// 给互斥量加锁
pthread_mutex_lock(&myMutex);
// 判断条件变量是否满足 访问公共区条件
while (0 == loopqueue.getSize())
{
pthread_cond_wait(&cond, &myMutex);
}
// 访问公共区, 取数据
// 往appsrc输入数据
buf = gst_buffer_new_allocate