繁体   English   中英

使用MediaCapture直播

[英]Live streaming using MediaCapture

对于一个项目,我正在尝试创建一个视频通话应用程序。 我正在为通用Windows平台做这个,所以我想我可以使用MediaCapture类。 这个类确实有StartRecordToCustomSinkAsync()方法,但为了使用它,我需要创建一个自定义接收器。 我开始创建一个,但现在我不得不创建流水槽。 此链接解释了Media Sinks,但我找不到关于流接收器的任何文档。

我还研究了Simple CommunicationWavSink另一个自定义接收器示例 ,但代码缺少注释或解决了另一个问题。

有谁知道我如何实施UWP视频通话应用程序,或指出我正确的方向?

PS。 我知道这类问题更多,但没有可用的答案:(

您的具体实施

实现您自己的IMFMediaSinkIMFStreamSink类的最重要的第一步是弄清楚您实际想要对您将要接收的IMFSample实例做什么。 一旦你知道你想要制作什么样的接收器,那么考虑你从哪里获取样本,无论是UWP MediaCapture类, IMFSinkWriterIMFTopology ,还是从客户端代码直接调用接收器。

如果您的目标是使用双向音频/视频聊天应用程序,则应用的每个实例都需要捕获音频和视频,以压缩格式对其进行编码,然后将这些样本发送到另一个实例。 除网络传输部分外, MediaCapture将负责几乎所有这些工作。 因此,另一个实例必须能够解码那些音频和视频样本,并将它们呈现给用户。 因此,你需要一个定制IMFMediaSink实现,它接收IMFSample从实例MediaCapture ,然后通过网络发送。 您还需要一个IMFMediaSource实现,它接收来自网络源的样本并将它们发送给演示者以呈现给用户。 在这种情况下,演示者将是MediaCapture一个实例。 每个应用实例都将同时运行两个自定义源和接收器 - 一个始终捕获音频和视频并通过网络发送,另一个从另一个应用实例接收并呈现它。

一种方法是为传输媒体接收器和接收媒体源提供套接字接口。 您需要找到一种方法来处理网络中断并干净地恢复数据传输和双方显示。 处理此问题的最简单方法之一是在实际的视频和音频样本传输之上设置轻量级消息传递层,以便您可以表示从发送方到接收方的流消息的开始,这将允许接收方转储任何排队的样本,并使用第一个到达的新样本重新启动其显示时钟,然后在此之后保持一致的显示。 同样, MediaCapture类可能会为您处理很多逻辑,但您必须小心,您的源和接收器的行为与MediaCapture期望的一样。

您还需要建立一个方案来缓冲每侧的数据,以便小的网络延迟不会导致音频和视频中的卡顿。 如果您以30 fps播放视频,则需要每33毫秒显示一个新帧,这意味着每个应用实例都需要缓冲足够的数据以保证演示者能够以该间隔显示帧。 音频大致相同 - 音频样本具有持续时间,因此您需要确保有足够的样本数据用于连续播放。 但是,你也不需要太多的缓冲,因为你会得到一个“卫星电视”效果,人类对话的心理造成每个人说话之间的巨大差距,因为他们正在等待你的应用程序传输的音频停止在他们开始谈话之前,缓冲延迟会放大这些差距。 Microsoft 在这里对缓冲进行了一般性的讨论,即使它特别适用于ASF流,它也很有用。

我最后一个实现相关的建议是看看Media Foundation可以使用的现有网络流格式。 虽然您仍然需要实现自己的源,媒体接收器和流接收器,但通过围绕现有Media Foundation实现编写支持WinRT的包装器,您可以避免担心绝大多数低级别细节。 我在这里看到的最简单的路径是编写一个实现IMFMediaSink的包装类,并保存使用MFCreateASFStreamingMediaSink创建的媒体接收器的内部副本。 您可能还需要编写一个启用WinRT的IMFByteSource实现,您可以将其提供给媒体接收器,然后将其传递到ASF流式接收器。 在源端,您将编写一个IMFMediaSource ,它从网络字节流中读取并包装ASF文件媒体源。


IMFMediaSinkIMFStreamSink概述

我无法为您提供能够满足您需求的代码,主要是因为实现自定义媒体接收器和一组流接收器需要大量工作,在这种情况下您的需求非常具体。 相反,我希望我能帮助您更好地理解Media Foundation中媒体接收器和流接收器的作用。 我离UWP专家很远,所以我会尽可能多地在媒体基金会方面做出贡献。

根据我对UWP的MediaCapture类的理解,自定义接收器负责与WinRT端和COM / Media Foundation端的接口,因此您必须在两端实现接口。 这是因为MediaCapture是一个UWP包装器,而不是很多被抽象出来的Media Foundation技术。 Simple Communication示例实际上非常有用,并且有很多很棒的入门代码,特别是在common / MediaExtensions部分中,它具有自定义网络媒体接收器的C ++实现。 该实现还向您展示了如何将自定义接收器与WinRT接口,以便它可以在UWP环境中使用。

下面是关于媒体和流接收器功能的一般性讨论,客户端代码使用它们的一般方式,以及它们的设计和实现的一般方法。


使用现有的IMFMediaSink

在本节中,我将介绍如何在实践中使用媒体接收器和流接收器。 这有望为Microsoft在构建Media Foundation的这一部分时所做的设计决策提供一些见解,并帮助您了解客户端代码(甚至您自己的客户端代码)将如何使用您的IMFMediaSinkIMFStreamSink自定义实现。


媒体接收器可以保存数据

让我们来看看你如何在Media Foundation中使用特定的媒体接收器实现。 我们将从您拨打MFCreateMPEG4MediaSink时收到的媒体接收器开始。 请记住,无论它们代表什么类型的媒体,所有媒体接收器都实现IMFMediaSink接口。 该功能创建了一个内置于Media Foundation的IMFMediaSink ,负责使用MPEG4容器结构创建输出。 创建它时,必须提供IMFByteStream ,输出MP4数据应写入其中。 媒体接收器负责维护IMFStreamSink对象的集合, IMFStreamSink对象用于媒体接收器的每个输入流。 在MPEG4媒体接收器的上下文中,仅存在两个可能的输出流,因此存在预定数量的流接收器。 这是因为MPEG4格式仅允许一个视频流和/或一个音频流。 通过这种方式,MPEG4媒体接收器强制执行各种合同 - 它限制客户端代码可以使用它来遵守它所编写的媒体容器的规范。 要访问这些接收器,您可以使用IMFMediaSink::GetStreamSinkCountIMFMediaSink::GetStreamSinkByIndex 如果您有音频和视频,则总共有2个接收器,其中索引0用于视频主要类型,索引1用于音频主要类型。

其他类型的媒体接收器,例如您在调用MFCreateASFMediaSink时获得的MFCreateASFMediaSink ,允许多个音频和视频流,并要求您为要写入媒体接收器的每个流调用IMFMediaSink::AddStreamSink 查看文档,您可以看到必须提供流接收器标识符(您要用于在介质接收器中引用该流的唯一DWORDint )和IMFMediaType ,以通知介质接收器您使用的是哪种数据我将发送该流水槽。 作为回报,您将收到IMFStreamSink ,并使用该流接收器为该流写入数据。

顺便说一句,你可以确定任意是否IMFMediaSink通过支持流的固定或可变数字IMFMediaSink::GetCharacteristics和检查输出标志MEDIASINK_FIXED_STREAMS 此外,大多数将数据存储到字节流的媒体接收器也会有一个名为MEDIASINK_RATELESS的标志,这意味着它将尽可能快地使用样本,因为它们都被写入字节流并且没有理由等待或者将该过程同步到演示时钟。 下一节将对此进行更多讨论。

这是一个简单的实例,一步一步。 如果您有H.264视频流和AAC音频流,每个都是一系列IMFSample实例,您可能希望将它们保存到硬盘驱动器上的文件中。 假设您想要使用ASF容器格式。 您将创建一个使用文件作为其存储方法的IMFByteStream ,然后使用MFCreateASFMediaSink创建一个ASF媒体接收器。 然后,您将调用IMFMediaSink::AddStreamSink来添加H.264视频流。 您将传入该流的唯一标识符,如0,以及指定媒体主要类型(视频),媒体子类型(H.264),帧大小和帧速率等内容的IMFMediaType ,等等。 您将收到一个IMFStreamSink ,您可以使用该特定的流接收器及其IMFStreamSink::ProcessSample方法发送所有H.264编码的视频样本。 然后,您再次为AAC音频流调用AddStreamSink ,并再次收到IMFStreamSink您可以使用它发送所有AAC编码的音频样本。 流接收器也知道他们的父IMFMediaSink是谁,并且每个流接收器与媒体接收器一起将数据打包成单个媒体流,其输出在您之前创建的IMFByteStream 设置好所有流接收器后,您将在每个接收器上重复调用IMFStreamSink::ProcessSample ,从IMFSample中提供相应类型(即视频或音频)的每个IMFSample


媒体汇集了数据

在这种情况下,存在意味着“在数据到达时呈现数据”。 这方面的一个例子是增强型视频渲染器(EVR),它实现了IMFMediaSink接口。 EVR负责通过其流接收器接收IMFSample实例,您可以通过IMFMediaSink::AddStreamSinkIMFMediaSink::GetStreamSinkByIndex它,就像我上面讨论的那样。 如果使用IMFMediaSink::GetCharacteristics查询EVR媒体接收器,您将看到它不提供MEDIASINK_RATELESS标志。 这是因为作为视频演示者的EVR应该将它接收的视频样本显示在屏幕上,并且它应该以每个IMFSample的开始时间和持续时间都得到尊重的方式进行。 这意味着EVR需要通过IMFPresentationClock接口的演示时钟,以便它能够以正确的帧速率显示视频样本并与时间的推移同步。 请记住,其工作只是存储数据的媒体接收器不必担心这一点,因为不需要以某种稳定或一致的速率存储数据。

有一个类似于EVR的音频渲染器,称为流式音频渲染器(SAR),它以类似的方式工作。 作为一个思想实验,如果您编写了自己的包含EVR和SAR的媒体接收器,请考虑基本架构,这样您就可以将视频和音频流连接到自定义接收器,并且自定义流接收器实现会将示例提供给相应的渲染器基于每个流接收器的媒体类型。 这样你就可以创建一个媒体接收器而不是担心EVR和SAR作为单独的媒体接收器。


实现自己的IMFMediaSinkIMFStreamSink

此时,我希望你能看到IMFStreamSink的实现完全依赖于IMFMediaSink的实现 - 换句话说,MPEG4媒体接收器需要自己特定的IMFStreamSink实现,因为那些流接收器需要打包原始MPEG4容器内的媒体数据,需要建立一个样本索引和嵌入MPEG4文件的时间戳,等等。


编写自己的MPEG4接收器版本

鉴于上面已经介绍过的内容,如果您正在编写自己的MPEG4接收器版本,请考虑如何构建隐藏在这些接口后面的实际类。 您需要IMFMediaSink的实现, IMFMediaSink是一种创建该实现的实例的方法,然后您需要有一个或多个IMFStreamSink实现, IMFStreamSink实现直接依赖于您的IMFMediaSink实现。

这是构建实现的一种方法。 您将从一个名为CMPEG4MediaSinkImpl的类开始,它实现了IMFMediaSink ,并且您可能有一个静态C风格的函数来创建该类的实例,就像MFCreateMPEG4MediaSink一样。 您只支持一个视频和一个音频流,并且每个媒体类型的媒体类型都提供给CMPEG4MediaSinkImpl类的非公共构造函数。 假设您希望根据媒体的主要类型(即音频或视频)设置不同的流接收器,可能是因为您需要单独计算您收到的音频和视频样本数量。 构造函数将检查每种媒体类型,对于视频流,它将创建CMPEG4StreamSinkVideoImpl的实例,对于音频,它将创建CMPEG4StreamSinkAudioImpl的实例。 每个类实现都将实现IMFStreamSink并负责在其IMFStreamSink::ProcessSample实现中接收各个IMFSample实例。 也许在这些实现中,每个流接收器将调用CMPEG4MediaSinkImpl::WriteSampleBlock ,它将创建适当的MPEG4数据结构来包装样本数据,以及在MPEG4结构中记录采样时间和持续时间,然后将其嵌入到输出IMFByteStream

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM