- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
前文已经简单介绍了Microsoft Media Foundation。下面我们使用它来实现一个简单的视频播放器(MF要求使用C/C++,不提供.NET接口).
初始化 。
在使用MF之前需要先初始化 。
HRESULT LT = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
MFStartup(MF_VERSION);
因为MF平台自身需要使用组件对象模型 (COM),所以需要先 用CoInitializeEx初始化COM,再调用MFStartup初始化MF平台.
既然有初始化过程,对应的也有销毁的过程(结束使用MF后) 。
MFShutdown();
CoUninitialize();
创建媒体源 。
第一步当然是指定MF要播放哪个文件.
MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID;
IMFSourceResolver* pSourceResolver;
IUnknown* pSource;
MFCreateSourceResolver(&pSourceResolver);
pSourceResolver->CreateObjectFromURL(filepath,MF_RESOLUTION_MEDIASOURCE, nullptr ,&objectType, &pSource);
m_pSource = (IMFMediaSource*)pSource;
创建IMFSourceResolver对象并调用它的 CreateObjectFromURL方法,传入文件路径filepath创建媒体源。得到的pSource指针指向的就是媒体源.
(注意:MF中的对象必须使用MFCreate******系列函数创建,后面可以看到一些其他的MFCreate函数) 。
然后我们把pSource转换成IMFMediaSource指针(m_pSource).
我们知道大多数的视频文件中不仅有视频流还有音频流。它们是完全不同的数据结构,所以我们需要不同的逻辑来对它们进行处理.
IMFPresentationDescriptor* pPresDescriptor;
DWORD nSourceStreams{ 0 };
m_pSource->CreatePresentationDescriptor(&pPresDescriptor);
pPresDescriptor->GetStreamDescriptorCount(&nSourceStreams);
for ( DWORD x = 0; x < nSourceStreams; x++) {
BuildTopology(pPresDescriptor, x);
}
使用CreatePresentationDescriptor函数获得媒体源的类型描述(文件容器描述).
使用GetStreamDescriptorCount函数就可以知道媒体源中包含了几个媒体流,然后用for循环分别处理.
注意:一个视频文件中只能确定至少包含一个视频流,可能有音频流(多数情况),可能有字幕流,并且视频流、音频流、字幕流都有可能包含多个(少数情况).
IMFStreamDescriptor* pStreamDescriptor;
IMFTopologyNode* pSourceNode;
IMFTopologyNode* pOutputNode;
BOOL streamSelected = FALSE;
pPresDescriptor->GetStreamDescriptorByIndex(nStream, &streamSelected, &pStreamDescriptor);
调用GetStreamDescriptorByIndex函数获得流的属性描述,其中nStream表示流的索引(前面循环中的x)。pStreamDescriptor指向的就是流的属性描述.
获得IMFPresentationDescriptor对象和IMFStreamDescriptor对象是不能省略的是必不可少的,它们提供对于创建管道提供了必不可少的信息.
创建管道 。
MFCreateTopology(&m_pTopology);
使用MFCreateTopology函数创建管道对象。(再次提醒:MF中对象的创建必须调用MFCreate系列函数) 。
MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pSourceNode);
pSourceNode->SetUnknown(MF_TOPONODE_SOURCE, m_pSource);
pSourceNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPresDescriptor);
pSourceNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pStreamDescriptor);
m_pTopology->AddNode(pSourceNode);
使用MFCreateTopologyNode并指明MF_TOPOLOGY_SOURCESTREAM_NODE创建管道中的媒体源节点,然后使用SetUnknown设置节点的属性。媒体源指向前面创建的m_pSource,同时设置它的类型属性(描述文件的类型描述和媒体流的类型描述).
调用AddNode把这个节点加入到指定的管道。至此媒体源已添加进管道中.
创建输出节点 。
前文提到MF平台支持将媒体流数据输出到文件、IO设备、网络等地方。这里我们就将流输出到电脑IO设备.
IMFMediaTypeHandler* pHandler;
IMFActivate* pRendererActivate{ nullptr };
GUID majorType = GUID_NULL;
pStreamDescriptor->GetMediaTypeHandler(&pHandler);
pHandler->GetMajorType(&majorType);
if (majorType == MFMediaType_Audio) {
MFCreateAudioRendererActivate(&pRendererActivate);
}
else if (majorType == MFMediaType_Video) {
MFCreateVideoRendererActivate(m_videoHwnd, &pRendererActivate);
}
从IMFStreamDescriptor获得IMFMediaTypeHandler对象,再调用GetMajorType即可获得流的MajorType,MajorType区分不同类型的流.
MFMediaType_Audio表示这是一个音频流,那就用MFCreateAudioRendererActivate创建表示音频渲染器 。
MFMediaType_Video表示这是一个视频流,那就用MFCreateVideoRendererActivate创建表示视频渲染器,其中m_videoHwnd代表窗口句柄.
然后把创建的pRendererActivate添加进管道.
MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pOutputNode);
pOutputNode->SetObject(pRendererActivate);
m_pTopology->AddNode(pOutputNode);<br>pSourceNode->ConnectOutput(0, pOutputNode, 0)
同样的创建管道节点使用MF_TOPOLOGY_OUTPUT_NODE标志着这是一个输出节点,并把它指向前面的pRendererActivate。然后调用AddNode将这个节点添加进管道,最后将源节点和输出节点相连.
至此我们就创建好了管道的输入节点和输出节点.
管道的作用 。
我们知道文件中的数据不管是音频流还是视频流都是经过压缩(编码后)保存的,电脑IO设备只能识别原始的媒体数据流,所以我们必须先解码了之后才能使用。又因为数据的编码方案有多种,所以我们必须先得知道是哪种编码类型(ARGB32、H264等)才能去找对应的解码器解码媒体流。但是这些工作管道会帮助我们完成,这就是管道最重要的功能:解析管道。管道会查找正确的解码器组件并加入到管道中补全管道.
原理就是管道会自我分析自身中的节点(组件),在视频文件播放器这个例子中,从媒体源中读取得到的是编码过的媒体流而IO设备只能识别原始的媒体数据流,所以管道能分析出这两个节点(组件)无法直接连在一起(组件的处理模式已在前文中介绍),必须找到这样一个组件:它的输入流是编码后的媒体流,它的输出是原始媒体流(也就是解码器类组件)然后把他放在媒体源组件和媒体呈现组件之间,这样就能正确处理了。而管道要找到这样的组件则必须要知道媒体源输出的数据流是什么,所以前面的代码中设置IMFPresentationDescriptor和IMFStreamDescriptor就是在告诉管道这些关于媒体源的数据流信息.
创建MFSession 。
MFCreateMediaSession( nullptr , &m_pSession);
m_pSession->SetTopology(0, m_topoBuilder.m_pTopology);
MFCreateMediaSession函数创建Session,然后使用SetTopology设置为我们刚创建完成的管道.
Session的作用前文已经提到:管理管道中媒体数据的流动。调用Session的Start方法开始启动数据流流动,在这个例子中就是开始播放视频。调用Pause方法暂停数据流动,在这个例子中就是暂停视频播放.
此外Session还可以实现倍数播放(也就是控制数据的流动速度),以及从指定位置开始播放等功能.
但是Session最重要的功能是数据流同步功能和资源管理功能。前面已经提到一个视频文件中有视频流和音频流,数据同步功能就是让视频流和音频流能够同步流动,资源管理就是Session负责创建和销毁管道中涉及到的对象(前面创建管道中的节点都是延迟创建,真正的创建由Session来管理).
总结
播放器的功能需要不同的组件配合完成任务,把组件装入管道中后,管道可以帮助我们分析是否缺少组件,如缺少会自动帮我们找到正确的组件补全管道.
使用IMSession管理创建好的IMFTopology可以更加方便的实现播放、暂停和其他高级功能等.
完整代码: https://github.com/lxy20221110/MMediaFoundation 。
。
最后此篇关于MediaFoundation播放器的文章就讲到这里了,如果你想了解更多关于MediaFoundation播放器的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
谁能详细说明以下问题? 蓝牙堆栈如何处理音频数据? 如何处理音频命令? 我们需要任何服务来处理音频数据吗? 提前致谢。 最佳答案 基本上,通过 BLE 的语音命令需要: 一些用于减少所需带宽的音频编解
我正在使用Player库以实现全屏视频播放。我相信它在幕后使用 AVFoundation。 我可以使用Float(self.player.maximumDuration)来实现视频的完整持续时间。但是
我正在制作一种宏记录器/播放器 我已经使用 java.awt.Robot() 等实用程序完成播放器部分,它模拟基本的人类鼠标/键盘输出命令,读取 XML 文件。 我卡在了必须记录该 XML 文件的部分
我目前有以下代码可以在页面上播放 youtube 视频。 //Load player api asynchronously. var tag = document.createElement('s
我需要提供音频内容(但不是音乐,更像是播客;人类语音),我正在考虑使用基于 Flash 的播放器让用户无需下载即可收听内容。 我需要一个免费的可嵌入 Flash 的 mp3 播放器。有什么建议? 因为
html5 player/api 更新了吗?事件 SC.Widget.Events.PLAY, SC.Widget.Events.PAUSE, SC.Widget.Events.FINISH, htm
我想在 Lubuntu VMware 中自动打开和关闭 vlc 播放器。我试过一个shell脚本代码,如: vlc rtmp://code sleep(5) exit 0 or vl
我有一个只支持纵向模式的应用程序,它有一个表格,每个单元格包含一个标题和一个带有 YouTube 视频的 web View 。 现在您将如何让 Youtube 播放器同时处于横向和纵向模式?
我正在尝试在我的应用程序中使用 YouTube 播放器 API,但我不知道如何确定视频是否为直播。如果有人知道如何获得视频的真实持续时间。 更新: 我想出了一种方法来确定内容是否是实时的,我使用我的后
我想创建一个能够播放 YouTube 视频的音频并将下载的内容保存在本地缓存中的应用程序,因此当用户决定恢复或再次播放视频时,它不必再次下载部分视频而只需下载剩余部分(用户可以决定如何处理缓存,以及如
我希望我的页面将 div 显示为模态,然后播放 YouTube 视频。我能够按预期播放视频(下面的代码),但是当我在过滤操作时切换到隐藏的 div 时,页面加载时隐藏的 div 不会将 data-sr
我正在尝试使用 AngularJS 和 WP API 构建 SPA。我使用部分在 ng-view 中加载我需要通过路由显示的所有内容。在此基础上,我添加了 Plangular,它是一个使用 Sound
我找到了一个不错的 HTML 5 音频播放器,它带有基于 plyr 的播放列表和艺术品。它在我的桌面浏览器上运行良好,但在我的移动设备 (iOS) 上,按播放后无法播放。有一个codepen来演示:
我正在尝试通过pyglet在Python 3中播放歌曲。我可以播放和停止播放一首歌曲,但是当我尝试播放下一首歌曲时会产生错误。 I followed these instructions.我将在tki
如何将嵌入的 Vimeo 视频重置为播放完毕后的加载状态? Vimeo API 提供了卸载方法 player.api("unload") 但它不适用于非 Flash 播放器。 最佳答案 使用Vimeo
我有一个用于音频录制和播放的网络应用程序。为此,我正在使用 html5 播放器。 现在我必须开发 Phonegap Android 应用程序。我已将插件(org.apache.cordova.medi
有人知道如何像 SuperFlix 一样将自己的字幕加载到 Netflix 播放器吗?关于 Netflix HTML5 播放器的信息很少,其中之一是我应该可以使用 操作播放器 netflix.cadm
如何将新的黑色 YouTube 播放器嵌入到我的网站(刚刚推出的网站)中? 我以前曾问过这个问题,但它已关闭,因为在投票否决和关闭之前没有人愿意真正阅读该问题。不,我没有问如何嵌入V2或V3播放器,我
几个小时以来,我一直在尝试添加一种打开我的 mp3 文件的方法并在队列中一一打开它们。但我不知道该怎么做。当涉及到单个文件时,我打开并播放不是问题。所以我正在考虑 Media(JavaFX) 类中的线
我知道这个函数 (setFullscreen) 只适用于 HTML5,但它对我不起作用。这是我使用的方式: setFullscreen: true 我希望 JW Player 在页面加载后立即以全屏模
我是一名优秀的程序员,十分优秀!