MediaStream图像捕捉

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MediaStream图像捕捉相关的知识,希望对你有一定的参考价值。

参考技术A MediaStream图像捕捉API 是一种用于从照相设备捕捉图像或视频的API。

1.通过调用获取对设备的引用 MediaDevices.getUserMedia() ;

2.隔离媒体流的可视部分;

通过调用来做到这一点 MediaStream.getVideoTracks() 。这将返回一个 MediaStreamTrack 对象数组。

3.如果希望在捕获图像之前配置设备功能,可以通过 applyConstraints() 在执行任何其他操作之前调用track 对象来完成此操作;

4.将MediaStreamTrack对象传递给 ImageCapture() 构造函数。

尽管 aMediaStream包含多种类型的轨道并提供多种检索它们的方法,但 ImageCapture 构造函数将抛出 a  DOMException of type NotSupportedErrorif  MediaStreamTrack.kind is not "video"。

ImageCapture     图像捕获,用于从通过有效引用的摄影设备捕获图像的接口 MediaStreamTrack 。

ImageCapture() 构造函数

创建一个新ImageCapture对象,该对象可用于从MediaStreamTrack表示视频流的给定对象中捕获 静止帧 (照片)。

特性:ImageCapture.track 只读,返回对 MediaStreamTrack (媒体流跟踪)传递给构造函数的引用。

ImageCapture.track   图像捕捉跟踪,接口的 track 只读属性 ImageCapture返回对MediaStreamTrack传递给 ImageCapture()构造函数的引用 。

该 MediaStreamTrack 接口表示流中的单个媒体轨道;通常,这些是音频或视频轨道,但也可能存在其他轨道类型。

接口的 takePhoto() 方法  ImageCapture 使用视频捕获设备进行单次曝光 MediaStreamTrack 并返回 Promise  一个 Blob 包含数据的解析。

licode学习之erizo篇--MediaStream

MediaStream是erizo进行流数据处理的核心模块。当网络数据,经过DtlsTransport进行srtp解密后,得到的rtp裸数据与rtcp裸数据,都要进入MediaStream进行处理;需要发送给对方的rtp数据与rtcp裸数据也要经过MediaStream处理后,才会给DtlsTransport进行加密并发送。

MediaStream也是个人认为erizo的最为复杂的一个部分。先看一看其集成体系:

class MediaStream: public MediaSink, public MediaSource, public FeedbackSink,
                        public FeedbackSource, public LogContext, public HandlerManagerListener,
                        public std::enable_shared_from_this<MediaStream>, public Service 

整个继承体系里面,涉及处理的基类有:MediaSink,MediaSource,FeedbackSink,FeedbackSource, HandlerManagerListener,Service

MediaStream同时承载着收数据处理,和发送数据处理两部分内容。其中和丢包重传等结合起来,就变为:接收rtp数据,发送rtcp重传信息;发送rtp数据,接收rtcp重传信息。

在接口上面,需要对外提供发送和接收到的裸数据回调。

这里面erizo的实现,是将MediaSink与MediaSource纠缠到一起的。但是从宏观上,我的理解是:

MediaSink:负责发送数据(write to client)

FeedbackSink:负责发送数据(write to client)

MediaSource:负责read出来rtp数据 (read from client)

FeedbackSource:负责read出来数据(read from client)

 

MediaStream继承MediaSink和FeedbackSink,所以直接调用MediaStream对象的deliverVideoData,deliverAudioData,deliverFeedback即可直接向对端发送数据。

要想接收对方的数据,需要MediaSource,FeedbackSource进行数据回调,怎么回调,需要进一步看一下MediaSource的定义:

**
 * A MediaSource is any class that produces audio or video data.
 */
class MediaSource: public virtual Monitor {
 protected:
    // SSRCs coming from the source
    uint32_t audio_source_ssrc_;
    std::vector<uint32_t> video_source_ssrc_list_;
    MediaSink* video_sink_;
    MediaSink* audio_sink_;
    MediaSink* event_sink_;
    // can it accept feedback
    FeedbackSink* source_fb_sink_;

 public:
    void setAudioSink(MediaSink* audio_sink) {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        this->audio_sink_ = audio_sink;
    }
    void setVideoSink(MediaSink* video_sink) {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        this->video_sink_ = video_sink;
    }
    void setEventSink(MediaSink* event_sink) {
      boost::mutex::scoped_lock lock(monitor_mutex_);
      this->event_sink_ = event_sink;
    }

    FeedbackSink* getFeedbackSink() {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        return source_fb_sink_;
    }
    virtual int sendPLI() = 0;
    uint32_t getVideoSourceSSRC() {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        if (video_source_ssrc_list_.empty()) {
          return 0;
        }
        return video_source_ssrc_list_[0];
    }
    void setVideoSourceSSRC(uint32_t ssrc) {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        if (video_source_ssrc_list_.empty()) {
          video_source_ssrc_list_.push_back(ssrc);
          return;
        }
        video_source_ssrc_list_[0] = ssrc;
    }
    std::vector<uint32_t> getVideoSourceSSRCList() {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        return video_source_ssrc_list_;  //  return by copy to avoid concurrent access
    }
    void setVideoSourceSSRCList(const std::vector<uint32_t>& new_ssrc_list) {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        video_source_ssrc_list_ = new_ssrc_list;
    }
    uint32_t getAudioSourceSSRC() {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        return audio_source_ssrc_;
    }
    void setAudioSourceSSRC(uint32_t ssrc) {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        audio_source_ssrc_ = ssrc;
    }

    bool isVideoSourceSSRC(uint32_t ssrc) {
      auto found_ssrc = std::find_if(video_source_ssrc_list_.begin(), video_source_ssrc_list_.end(),
          [ssrc](uint32_t known_ssrc) {
          return known_ssrc == ssrc;
          });
      return (found_ssrc != video_source_ssrc_list_.end());
    }

    bool isAudioSourceSSRC(uint32_t ssrc) {
      return audio_source_ssrc_ == ssrc;
    }

    MediaSource() : audio_source_ssrc_{0}, video_source_ssrc_list_{std::vector<uint32_t>(1, 0)},
      video_sink_{nullptr}, audio_sink_{nullptr}, event_sink_{nullptr}, source_fb_sink_{nullptr} {}
    virtual ~MediaSource() {}

    virtual void close() = 0;
};

MediaSource定义了4个MediaSink对象,分别对应video,audio,event,feedback。

MediaStream继承了MediaSource同样的4个set接口,让调用者可以设置MediaSource的这4个对象。当发生读取事件时,MediaStream会调用这4个设置的MediaSink对象的相应方法,来向外传递数据。

FeedbackSource也是同样的道理。

void MediaStream::read(std::shared_ptr<DataPacket> packet) {
  char* buf = packet->data;
  int len = packet->length;
  // PROCESS RTCP
  RtpHeader *head = reinterpret_cast<RtpHeader*> (buf);
  RtcpHeader *chead = reinterpret_cast<RtcpHeader*> (buf);
  uint32_t recvSSRC = 0;
  if (!chead->isRtcp()) {
    recvSSRC = head->getSSRC();
  } else if (chead->packettype == RTCP_Sender_PT || chead->packettype == RTCP_SDES_PT) {  // Sender Report
    recvSSRC = chead->getSSRC();
  }
  // DELIVER FEEDBACK (RR, FEEDBACK PACKETS)
  if (chead->isFeedback()) {
    if (fb_sink_ != nullptr && should_send_feedback_) {
      fb_sink_->deliverFeedback(std::move(packet));
    }
  } else {
    // RTP or RTCP Sender Report
    if (bundle_) {
      // Check incoming SSRC
      // Deliver data
      if (isVideoSourceSSRC(recvSSRC) && video_sink_) {
        parseIncomingPayloadType(buf, len, VIDEO_PACKET);
        video_sink_->deliverVideoData(std::move(packet));
      } else if (isAudioSourceSSRC(recvSSRC) && audio_sink_) {
        parseIncomingPayloadType(buf, len, AUDIO_PACKET);
        audio_sink_->deliverAudioData(std::move(packet));
      } else {
        ELOG_DEBUG("%s read video unknownSSRC: %u, localVideoSSRC: %u, localAudioSSRC: %u",
                    toLog(), recvSSRC, this->getVideoSourceSSRC(), this->getAudioSourceSSRC());
      }
    } else {
      if (packet->type == AUDIO_PACKET && audio_sink_) {
        parseIncomingPayloadType(buf, len, AUDIO_PACKET);
        // Firefox does not send SSRC in SDP
        if (getAudioSourceSSRC() == 0) {
          ELOG_DEBUG("%s discoveredAudioSourceSSRC:%u", toLog(), recvSSRC);
          this->setAudioSourceSSRC(recvSSRC);
        }
        audio_sink_->deliverAudioData(std::move(packet));
      } else if (packet->type == VIDEO_PACKET && video_sink_) {
        parseIncomingPayloadType(buf, len, VIDEO_PACKET);
        // Firefox does not send SSRC in SDP
        if (getVideoSourceSSRC() == 0) {
          ELOG_DEBUG("%s discoveredVideoSourceSSRC:%u", toLog(), recvSSRC);
          this->setVideoSourceSSRC(recvSSRC);
        }
        // change ssrc for RTP packets, don‘t touch here if RTCP
        video_sink_->deliverVideoData(std::move(packet));
      }
    }  // if not bundle
  }  // if not Feedback
}

这里,MediaStream的读写就清楚了,如果我们需要使用MediaStream,则需要做:

1、定义一个MediaSink的子类,将之设置给MediaStream,用于接收MediaStream的数据

2、直接调用MediaStream的deliver方法,让其向外发送数据。

class Receiver : public MediaSink
{
public:
    virtual void close() {};

private:
    virtual int deliverAudioData_(std::shared_ptr<DataPacket> data_packet) {
        printf("now receiver audio packet
");
    };
    virtual int deliverVideoData_(std::shared_ptr<DataPacket> data_packet) {
        printf("now receive video packet
");
    };
    virtual int deliverEvent_(MediaEventPtr event) {
        printf("now receive event packet 
");
    };
};
class StreamControl
{
public:
    void init() {
        m_workerPool = std::make_shared<ThreadPool>(2);
        m_ioWorkerPool = std::make_shared<IOThreadPool>(2);

        std::string connid = "1";
        IceConfig cfg;//you may need init the cfg value
        std::vector<RtpMap> rtp_mappings;//you may need to init the mappings
        std::vector<erizo::ExtMap> ext_mappings; //you may need to init the ext mappings
        WebRtcConnectionEventListener* listener = new SendOfferEvtListener;
        m_conn = std::make_shared<WebRtcConnection>(workerPool->getLessUsedWorker(),
            m_ioWorkerPool->getLessUsedIOWorker(),
            connid,
            cfg,
            rtp_mappings,
            ext_mappings,
            listener);
        std::string stream_id = "1";
        std::string stream_label = "1";
        m_pStream = new MediaStream(m_workerPool->getLessUsedWorker(),
            m_conn,
            stream_id,
            stream_label,
            true);
        m_conn->addMediaStream(m_pStream);
        m_pStream->setVideoSink(&m_receiver);
        m_pStream->setAudioSink(&m_receiver);
        m_pStream->setEventSink(&m_receiver);
    }

    void sendRtpData(const char* buf, int len, bool isVideo) {
        std::shared_ptr<DataPacket> dp = std::make_shared<DataPacket>();
        dp->length = len;
        memcpy(dp->data, buf);
        dp->comp = 1;
        dp->type = isVideo ? VIDEO_PACKET : AUDIO_PACKET;
        if (isVideo)
        {
            m_pStream->deliverVideoData(dp);
        }
        else
        {
            m_pStream->deliverAudioData(dp);
        }
    }
private:
    Receiver m_receiver;
    MediaStream* m_pStream;
    std::shared_ptr<ThreadPool> m_workerPool;
    std::shared_ptr<IOThreadPool> m_ioWorkerPool;
    WebRtcConnection* m_conn;
};

 

MediaStream还继承了HandlerManagerListener,Service这两部分是媒体处理的核心模块,pipeline使用的内容,之后学习时再写吧。pipeline更是复杂,代码好难读。

以上是关于MediaStream图像捕捉的主要内容,如果未能解决你的问题,请参考以下文章

通过 NodeJS 发送 MediaStream

如何开始运行 mediaStream

是否可以从 .wav 文件创建 MediaStream?

将音频和视频轨道合并到新的 MediaStream 中

从画布和视频元素创建 MediaStream

无法通过 WebRTC 听到 MediaStream