【HAS】DASH库libdash代码结构解析
2017-03-04 01:32
218 查看
libdash分析
libdash是bitmovin公司开源的一个DASH库,完整实现了DASH协议,可以在Github上获取其代码。同时,该开源库还基于QT开发了一个播放器,之前做DASH自适应算法的时候,在该实例播放器上实现过算法,也对该播放器的代码结构较为熟悉,特记录下来,本文将主要针对设计自适应算法部分的代码。整体结构
每一个类的作用
1. DASHPlayer
class DASHPlayer : public IDASHPlayerGuiObserver, public managers::IMultimediaManagerObserver
这个sample player用的最多的一种设计模式就是观察者模式,可以看到几乎下面每一个类都是继承了几个观察者类,通过观察者这种形式,来获取其包含的类的状态信息,包括下载状态、buffer状态以及播放器按钮等。DASHPlayer继承了IDASHPlayerGuiObserver和IMultimediaManagerObserver,其将观察DASHPlayerGui和MultimediaManager两个类的状态,分别对应着下面两组函数。同时,DASHPlayer主要的两个包含的类就是QtSamplePlayerGui和MultimediaManager,前者负责QT界面的功能,后者负责媒体的下载和播放。
virtual void OnSettingsChanged (int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); virtual void OnStartButtonPressed (int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); virtual void OnStopButtonPressed (); virtual void OnDownloadMPDPressed (const std::string &url);
当QT界面的DownloadMPD按钮按下的话,将执行该函数里的内容,其余的类似,分别是Start, Stop, Setting所对应的功能。下面简单介绍一下,OnDownloadMPDPressed函数的功能,
void DASHPlayer::OnDownloadMPDPressed (const std::string &url) { if(!this->multimediaManager->Init(url)) { this->gui->SetStatusBar("Error parsing mpd at: " + url); return; // TODO dialog or symbol that indicates that error } this->SetSettings(-1, -1, -1, -1, -1); this->gui->SetStatusBar("Successfully parsed MPD at: " + url); this->gui->SetGuiFields(this->multimediaManager->GetMPD()); }
首先,会初始化multimediaManager中的相关内容,其中就包括mpd的下载等,另外,会对QT界面gui进行一些设置,改变按钮的状态之类的。
/* IMultimediaManagerObserver */ virtual void OnVideoBufferStateChanged (uint32_t fillstateInPercent); virtual void OnVideoSegmentBufferStateChanged (uint32_t fillstateInPercent); virtual void OnAudioBufferStateChanged (uint32_t fillstateInPercent); virtual void OnAudioSegmentBufferStateChanged (uint32_t fillstateInPercent);
这几个函数,负责监视MultimediaManager的状态,相应的状态发生改变时,会执行其中的函数,主要是设计到QT界面上关于buffer部分的显示等。
bool SettingsChanged (int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); void SetSettings (int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation);
DASHPlayer也可以更改将要下载的视频的质量,主要是上面两个函数来实现的,其主要是为了实现通过QT界面来调节的功能,但是,自适应算法最好不要在这里实现,太顶层了,而且,整个代码结构一直都是通过观察者传递,传到这一层会经过很多的类,写起来很麻烦。
2. MultimediaManager
MultimediaManager的功能比较强大,负责三个方面:
一,负责下载音视频片段,主要的类是
MultimediaStream,针对每一个视频流或者音频流都会创建一个
MultimediaStream,所以,具体自适应逻辑应该作用在在
MultimediaStream中。这一部分的介绍,将放在下一章节里讲述。
二,负责音视频的渲染,有两个创建音频和视频渲染线程的函数如下,其主要是调用了ffmpeg的类库,
/* Threads for Rendering Audio & Video */ bool StartVideoRenderingThread (); void StopVideoRenderingThread (); static void* RenderVideo (void *data); bool StartAudioRenderingThread (); void StopAudioRenderingThread (); static void* RenderAudio (void *data);
可以看一下,RenderVideo这个函数的内容,
void* MultimediaManager::RenderVideo (void *data) { MultimediaManager *manager = (MultimediaManager*) data; QImage *frame = manager->videoStream->GetFrame(); while(manager->isVideoRendering) { if (frame) { manager->videoElement->SetImage(frame); manager->videoElement->update(); manager->framesDisplayed++; PortableSleep(1 / manager->frameRate); delete(frame); } frame = manager->videoStream->GetFrame(); } return NULL; }
此处一定要分清manager是 MultimediaManager本身,而不是DASHManger,可以参考第一行代码,
MultimediaManager *manager = (MultimediaManager*) data;,
可以看到,manager存在两个成员变量,
manager->videoElement->update();, videoElement负责更新每一帧图像,
manager->videoStream->GetFrame();, videoStream负责获取下一帧图像。
三、负责自适应逻辑的设置和调整,具体相关的函数如下,
void MultimediaManager::InitVideoRendering (uint32_t offset) { this->videoLogic = AdaptationLogicFactory::Create(libdash::framework::adaptation::Manual, this->mpd, this->period, this->videoAdaptationSet); this->videoStream = new MultimediaStream(sampleplayer::managers::VIDEO, this->mpd, SEGMENTBUFFER_SIZE, 2, 0); this->videoStream->AttachStreamObserver(this); this->videoStream->SetRepresentation(this->period, this->videoAdaptationSet, this->videoRepresentation); this->videoStream->SetPosition(offset); }
在初始化的时候,就设置了自适应逻辑,只要修改
libdash::framework::adaptation::Manual的值,即可通过简单工厂模式,选择不同的自适应逻辑。目前库里自带的逻辑都是最简单的实例性质的。
如果在运行中修改自适应逻辑的话,可以根据下面两个函数,当然现在都没有具体的实现,可以参考上边的创建来实例化一个自适应逻辑并赋值给
videoLogic即可。
bool SetVideoAdaptationLogic (libdash::framework::adaptation::LogicType type); bool SetAudioAdaptationLogic (libdash::framework::adaptation::LogicType type);
3. MultimediaStream
该类负责一路音频或者视频流,主要的功能有两个方面,一、负责控制下载或者暂停,主要函数如下,具体就是控制
DASHManger来实现,
bool MultimediaStream::StartDownload () { if(!dashManager->Start()) return false; return true; } void MultimediaStream::StopDownload () { this->dashManager->Stop(); }
二、存储解码好的视频帧,并负责交给上层
MultimediaManger渲染,涉及到的代码如下,
libdash::framework::buffer::Buffer<QImage> *frameBuffer; libdash::framework::buffer::Buffer<libdash::framework::buffer::AudioChunk> *sampleBuffer; void AddFrame (QImage *frame); QImage* GetFrame ();
4. DASHManger
DASHManger具体控制下载和解码,主要的成员有,
buffer::MediaObjectBuffer *buffer; MediaObjectDecoder *mediaObjectDecoder; DASHReceiver *receiver;
其中
DASHReceiver获取到的MediaObject会被放在
buffer中,
MediaObjectDecoder负责解码
buffer中的数据,并放到上边那个类的frameBuffer中。
5. DASHReceiver
这个类,涉及到下载的最顶层的内容,主要看doBuffering这个线程,里边涉及到每一个块的下载,故而,可以在此处计算下载每一个块所用的时间,然后,就可以估计网络状态,是个很重要的变量,
/* Thread that does the buffering of segments */ void* DASHReceiver::DoBuffering (void *receiver) { DASHReceiver *dashReceiver = (DASHReceiver *) receiver; dashReceiver->DownloadInitSegment(dashReceiver->GetRepresentation()); MediaObject *media = dashReceiver->GetNextSegment(); while(media != NULL && dashReceiver->isBuffering) { media->StartDownload(); if (!dashReceiver->buffer->PushBack(media)) return NULL; media->WaitFinished(); dashReceiver->NotifySegmentDownloaded(); media = dashReceiver->GetNextSegment(); } dashReceiver->buffer->SetEOS(true); return NULL; }
到此处,涉及到自适应逻辑的每一个类都讲过了,那么,自适应逻辑该加在什么地方呢?其实可以加的地方还是蛮多的,而且,对性能影响也不大,如果设计的是,每下载完一个segment做一次逻辑的话,可以放在
OnSegmentDownloaded这个函数里,我是将逻辑放在
MultimediaManger中的,因为涉及到音频流和视频流,要做一次流判断,可以在
MultimediaManger的
OnSegmentDownloaded中如下添加代码,
void MultimediaManager::OnSegmentDownloaded (StreamType type, double current_bandwidth) { this->segmentsDownloaded++; estimate_bandwidth.push_back(current_bandwidth); switch (type) { case AUDIO: this->audioLogic->EstimateBandwidth(estimate_bandwidth); this->audioLogic->DoLogic(); this->SetAudioQuality(this->period, this->audioLogic->GetAdaptationSet(), this->audioLogic->GetRepresentation()); break; case VIDEO: this->videoLogic->EstimateBandwidth(estimate_bandwidth); this->videoLogic->DoLogic(); this->SetVideoQuality(this->period, this->videoLogic->GetAdaptationSet(), this->videoLogic->GetRepresentation()); break; default: break; } }
我在自己的Github仓库里,大概把该有的内容都添加进来了,只需要在逻辑类做相应的实现即可,现在逻辑类可以获取的参数有,buffer的状态以及预测带宽,这两块的具体实现以及自适应算法的实现都很有意义,欢迎大家一起来研究讨论这些问题。
更多内容可以关注我的个人博客地址:【HAS】DASH库libdash代码结构解析
相关文章推荐
- wojilu系统的ORM代码解析-[源代码结构分析,用特性和反射来感知属性-特性介绍篇]
- Wayland 源码解析之代码结构
- faster-rcnn 之 RPN网络的结构解析以及RPN代码详解
- 以太网报文结构分析,与解析代码
- 使用递归算法结合数据库解析成Java树形结构的代码解析
- TS结构解析(详细的PAT和PMT解析代码)
- QQwry.dat 数据结构 存储结构 解析[C#代码]
- jquery源码解析:代码结构分析
- Android之高仿雅虎天气(二)---代码结构解析
- java数据结构之树基本概念解析及代码示例
- 红黑树结构算法原理与代码解析
- mokid 工程代码下载与结构分析与两种机理的简单解析
- Android之高仿雅虎天气(二)---代码结构解析
- OpenLTE开源代码结构解析(二)_澄清
- C# IFF图形结构解析代码
- RocketMQ源码解析(1)--代码结构与模块说明
- netlink监听网络变化代码(转载)+流程分析(原创+转载)+数据结构以及相关宏的解析(原创)
- 详细解读PHP解析XML元素结构的代码示例
- Android之高仿雅虎天气(二)---代码结构解析
- wojilu系统的ORM代码解析-[源代码结构分析,ObjectBase基类分析]