Qt编写万能硬解播放器/支持传入QByteArray流数据解码/内存流

360影视 欧美动漫 2025-05-12 23:44 2

摘要:用纯Qt+ffmpeg打造的这个播放组件,在各种复杂的环境中测试,满足各种音视频格式和场景需求,后期又多了一种需求,那就是没有音视频地址的,直接是内存流数据,比如有个下位机,直接通过tcp或者udp收到的264/265裸流数据,很多时候可能还是私有协议上传的,

用纯Qt+ffmpeg打造的这个播放组件,在各种复杂的环境中测试,满足各种音视频格式和场景需求,后期又多了一种需求,那就是没有音视频地址的,直接是内存流数据,比如有个下位机,直接通过tcp或者udp收到的264/265裸流数据,很多时候可能还是私有协议上传的,需要解包后才是裸流数据,然后希望直接用ffmpeg来解码。按照常规的做法,就是先打开一个地址,再去解码,网上也基本上都是这种示例,而如果是内存流数据,根本没有地址,如何处理?查阅了大量资料发现ffmpeg肯定提供了对应的接口,那就是AVIOContext和AVFifo,这种专用于处理内存流数据,和之前的解码框架流程几乎一致,唯一区别就两个,一个是需要增加AVFormatContext的回调formatCtx->pb = avio->getCtx,然后就是打开的时候对应地址参数填空值,avformat_open_input(&formatCtx, NULL, NULL, &options),在创建内存流上下文对象的时候指定读取回调函数read_packet,avioCtx = avio_alloc_context(avioBuf, fifoSize, 0, fifo, &read_packet, NULL, NULL),收到数据后往这个环形缓冲数据添加数据即可。

如何在之前的框架上增加这个功能,以便用户使用起来非常方便,那就需要增加一种地址格式比如stream://就表示内存流,一旦用户调用open函数填入地址stream://就表示接下来打开的是内存流。然后只要对外提供一个appenddata函数即可,不断的往里面追加数据,追加的数据会自动解码。

内核ffmpeg和内核mdk支持内存流数据解码。内存流数据一般指264/265裸流数据。比如tcp或者udp收到下位机传过来的数据。或者自己根据私有协议解包后的数据。还有ws://协议开头的websocket收到的数据。以及监控国标gb28181协议中rtp解包后的ps流数据。只要是ffmpeg支持的内存流数据就可以。 //表示接下来打开的是内存流w.open("stream://");//然后收到数据后添加到对应解码线程即可QByteArray data = "xxxxx";w.getVideoThread->appendData(data);//特别提示/记得判断解码线程对象是否为空/不为空才可以调用//什么时候肯定不为空/在收到receivePlayStart信号后VideoThread *videoThread = w.getVideoThread;if (videoThread) {videoThread->appendData(data);}

在这里插入图片描述

在这里插入图片描述

#include "ffmpegavio.h"#include "videowebsocket.h"void FFmpegAvio::init(FFmpegAvio **obj, const QString &url, const QString &flag, MediaType mediaType){if (mediaType == MediaType_Stream || mediaType == MediaType_WebSocket) {if (!(*obj)) {(*obj) = new FFmpegAvio;(*obj)->setObjectName(QString("FFmpegAvio_%1").arg(flag));}(*obj)->setUrl(url, mediaType);(*obj)->start;}}void FFmpegAvio::free(FFmpegAvio **obj){if ((*obj)) {(*obj)->quit;(*obj)->wait;(*obj)->free;(*obj)->deleteLater;(*obj) = NULL;}}FFmpegavio::FFmpegAvio(QObject *parent) : QThread(parent){//可以自行调整下面几个值connTimeout = 1500;//值越小打开速度越快/分辨率越大值建议越大minSizeOpen = 100 * 1024;//值越小越容易花屏/控制多大缓存才能读取minSizeRead = 1 * 1024;//整个缓存队列的大小fifoSize = 10 * 1024 * 1024;fifo = NULL;avioCtx = NULL;//实例化网络数据通信对象videoWeb = new VideoWebSocket(this);connect(videoWeb, SIGNAL(binaryMessageReceived(QByteArray)), this, SLOT(append(QByteArray)));}FFmpegAvio::~FFmpegAvio{}void FFmpegAvio::run{if (mediaType == MediaType_WebSocket) {videoWeb->initWeb;videoWeb->closeWeb;}this->freeFifo;this->initFifo;if (mediaType == MediaType_WebSocket) {videoWeb->openWeb(this->url, this->connTimeout);}this->exec;}void FFmpegAvio::initFifo{if (!fifo) {#if (FFMPEG_VERSION_MAJOR buffer);#if (FFMPEG_VERSION_MAJOR checkData(minSizeOpen);}bool FFmpegAvio::canRead{return this->checkData(minSizeRead);}bool FFmpegAvio::checkData(int size){if (!fifo) {return false;}#if (FFMPEG_VERSION_MAJOR size);#elsereturn (av_fifo_can_read(fifo) > size);#endif}AVIOContext *FFmpegAvio::getCtx{return this->avioCtx;}void FFmpegAvio::setUrl(const QString &url, MediaType mediaType){this->url = url;this->mediaType = mediaType;if (mediaType == MediaType_Stream) {minSizeRead = 100 * 1024;fifoSize = 100 * 1024 * 1024;}}void FFmpegAvio::reopen{this->quit;this->wait;this->start;}void FFmpegAvio::free{if (mediaType == MediaType_WebSocket) {videoWeb->freeWeb;}this->freeFifo;}

来源:Qt自定义控件

相关推荐