media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // Original author: ekr@rtfm.com
michael@0 7
michael@0 8 #include "logging.h"
michael@0 9 #include "MediaPipeline.h"
michael@0 10
michael@0 11 #ifndef USE_FAKE_MEDIA_STREAMS
michael@0 12 #include "MediaStreamGraphImpl.h"
michael@0 13 #endif
michael@0 14
michael@0 15 #include <math.h>
michael@0 16
michael@0 17 #include "nspr.h"
michael@0 18 #include "srtp.h"
michael@0 19
michael@0 20 #ifdef MOZILLA_INTERNAL_API
michael@0 21 #include "VideoSegment.h"
michael@0 22 #include "Layers.h"
michael@0 23 #include "ImageTypes.h"
michael@0 24 #include "ImageContainer.h"
michael@0 25 #include "VideoUtils.h"
michael@0 26 #ifdef WEBRTC_GONK
michael@0 27 #include "GrallocImages.h"
michael@0 28 #include "mozilla/layers/GrallocTextureClient.h"
michael@0 29 #endif
michael@0 30 #endif
michael@0 31
michael@0 32 #include "nsError.h"
michael@0 33 #include "AudioSegment.h"
michael@0 34 #include "MediaSegment.h"
michael@0 35 #include "databuffer.h"
michael@0 36 #include "transportflow.h"
michael@0 37 #include "transportlayer.h"
michael@0 38 #include "transportlayerdtls.h"
michael@0 39 #include "transportlayerice.h"
michael@0 40 #include "runnable_utils.h"
michael@0 41 #include "libyuv/convert.h"
michael@0 42 #include "mozilla/gfx/Point.h"
michael@0 43 #include "mozilla/gfx/Types.h"
michael@0 44
michael@0 45 #include "webrtc/modules/interface/module_common_types.h"
michael@0 46 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
michael@0 47
michael@0 48 using namespace mozilla;
michael@0 49 using namespace mozilla::gfx;
michael@0 50
michael@0 51 // Logging context
michael@0 52 MOZ_MTLOG_MODULE("mediapipeline")
michael@0 53
michael@0 54 namespace mozilla {
michael@0 55
michael@0 56 static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp";
michael@0 57
michael@0 58 MediaPipeline::~MediaPipeline() {
michael@0 59 ASSERT_ON_THREAD(main_thread_);
michael@0 60 MOZ_ASSERT(!stream_); // Check that we have shut down already.
michael@0 61 MOZ_MTLOG(ML_INFO, "Destroying MediaPipeline: " << description_);
michael@0 62 }
michael@0 63
michael@0 64 nsresult MediaPipeline::Init() {
michael@0 65 ASSERT_ON_THREAD(main_thread_);
michael@0 66
michael@0 67 RUN_ON_THREAD(sts_thread_,
michael@0 68 WrapRunnable(
michael@0 69 nsRefPtr<MediaPipeline>(this),
michael@0 70 &MediaPipeline::Init_s),
michael@0 71 NS_DISPATCH_NORMAL);
michael@0 72
michael@0 73 return NS_OK;
michael@0 74 }
michael@0 75
michael@0 76 nsresult MediaPipeline::Init_s() {
michael@0 77 ASSERT_ON_THREAD(sts_thread_);
michael@0 78 conduit_->AttachTransport(transport_);
michael@0 79
michael@0 80 nsresult res;
michael@0 81 MOZ_ASSERT(rtp_.transport_);
michael@0 82 MOZ_ASSERT(rtcp_.transport_);
michael@0 83 res = ConnectTransport_s(rtp_);
michael@0 84 if (NS_FAILED(res)) {
michael@0 85 return res;
michael@0 86 }
michael@0 87
michael@0 88 if (rtcp_.transport_ != rtp_.transport_) {
michael@0 89 res = ConnectTransport_s(rtcp_);
michael@0 90 if (NS_FAILED(res)) {
michael@0 91 return res;
michael@0 92 }
michael@0 93 }
michael@0 94
michael@0 95 if (possible_bundle_rtp_) {
michael@0 96 MOZ_ASSERT(possible_bundle_rtcp_);
michael@0 97 MOZ_ASSERT(possible_bundle_rtp_->transport_);
michael@0 98 MOZ_ASSERT(possible_bundle_rtcp_->transport_);
michael@0 99
michael@0 100 res = ConnectTransport_s(*possible_bundle_rtp_);
michael@0 101 if (NS_FAILED(res)) {
michael@0 102 return res;
michael@0 103 }
michael@0 104
michael@0 105 if (possible_bundle_rtcp_->transport_ != possible_bundle_rtp_->transport_) {
michael@0 106 res = ConnectTransport_s(*possible_bundle_rtcp_);
michael@0 107 if (NS_FAILED(res)) {
michael@0 108 return res;
michael@0 109 }
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 return NS_OK;
michael@0 114 }
michael@0 115
michael@0 116
michael@0 117 // Disconnect us from the transport so that we can cleanly destruct the
michael@0 118 // pipeline on the main thread. ShutdownMedia_m() must have already been
michael@0 119 // called
michael@0 120 void MediaPipeline::ShutdownTransport_s() {
michael@0 121 ASSERT_ON_THREAD(sts_thread_);
michael@0 122 MOZ_ASSERT(!stream_); // verifies that ShutdownMedia_m() has run
michael@0 123
michael@0 124 disconnect_all();
michael@0 125 transport_->Detach();
michael@0 126 rtp_.transport_ = nullptr;
michael@0 127 rtcp_.transport_ = nullptr;
michael@0 128 possible_bundle_rtp_ = nullptr;
michael@0 129 possible_bundle_rtcp_ = nullptr;
michael@0 130 }
michael@0 131
michael@0 132 void MediaPipeline::StateChange(TransportFlow *flow, TransportLayer::State state) {
michael@0 133 TransportInfo* info = GetTransportInfo_s(flow);
michael@0 134 MOZ_ASSERT(info);
michael@0 135
michael@0 136 if (state == TransportLayer::TS_OPEN) {
michael@0 137 MOZ_MTLOG(ML_INFO, "Flow is ready");
michael@0 138 TransportReady_s(*info);
michael@0 139 } else if (state == TransportLayer::TS_CLOSED ||
michael@0 140 state == TransportLayer::TS_ERROR) {
michael@0 141 TransportFailed_s(*info);
michael@0 142 }
michael@0 143 }
michael@0 144
michael@0 145 static bool MakeRtpTypeToStringArray(const char** array) {
michael@0 146 static const char* RTP_str = "RTP";
michael@0 147 static const char* RTCP_str = "RTCP";
michael@0 148 static const char* MUX_str = "RTP/RTCP mux";
michael@0 149 array[MediaPipeline::RTP] = RTP_str;
michael@0 150 array[MediaPipeline::RTCP] = RTCP_str;
michael@0 151 array[MediaPipeline::MUX] = MUX_str;
michael@0 152 return true;
michael@0 153 }
michael@0 154
michael@0 155 static const char* ToString(MediaPipeline::RtpType type) {
michael@0 156 static const char* array[(int)MediaPipeline::MAX_RTP_TYPE] = {nullptr};
michael@0 157 // Dummy variable to cause init to happen only on first call
michael@0 158 static bool dummy = MakeRtpTypeToStringArray(array);
michael@0 159 (void)dummy;
michael@0 160 return array[type];
michael@0 161 }
michael@0 162
michael@0 163 nsresult MediaPipeline::TransportReady_s(TransportInfo &info) {
michael@0 164 MOZ_ASSERT(!description_.empty());
michael@0 165
michael@0 166 // TODO(ekr@rtfm.com): implement some kind of notification on
michael@0 167 // failure. bug 852665.
michael@0 168 if (info.state_ != MP_CONNECTING) {
michael@0 169 MOZ_MTLOG(ML_ERROR, "Transport ready for flow in wrong state:" <<
michael@0 170 description_ << ": " << ToString(info.type_));
michael@0 171 return NS_ERROR_FAILURE;
michael@0 172 }
michael@0 173
michael@0 174 MOZ_MTLOG(ML_INFO, "Transport ready for pipeline " <<
michael@0 175 static_cast<void *>(this) << " flow " << description_ << ": " <<
michael@0 176 ToString(info.type_));
michael@0 177
michael@0 178 // TODO(bcampen@mozilla.com): Should we disconnect from the flow on failure?
michael@0 179 nsresult res;
michael@0 180
michael@0 181 // Now instantiate the SRTP objects
michael@0 182 TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
michael@0 183 info.transport_->GetLayer(TransportLayerDtls::ID()));
michael@0 184 MOZ_ASSERT(dtls); // DTLS is mandatory
michael@0 185
michael@0 186 uint16_t cipher_suite;
michael@0 187 res = dtls->GetSrtpCipher(&cipher_suite);
michael@0 188 if (NS_FAILED(res)) {
michael@0 189 MOZ_MTLOG(ML_ERROR, "Failed to negotiate DTLS-SRTP. This is an error");
michael@0 190 info.state_ = MP_CLOSED;
michael@0 191 UpdateRtcpMuxState(info);
michael@0 192 return res;
michael@0 193 }
michael@0 194
michael@0 195 // SRTP Key Exporter as per RFC 5764 S 4.2
michael@0 196 unsigned char srtp_block[SRTP_TOTAL_KEY_LENGTH * 2];
michael@0 197 res = dtls->ExportKeyingMaterial(kDTLSExporterLabel, false, "",
michael@0 198 srtp_block, sizeof(srtp_block));
michael@0 199 if (NS_FAILED(res)) {
michael@0 200 MOZ_MTLOG(ML_ERROR, "Failed to compute DTLS-SRTP keys. This is an error");
michael@0 201 info.state_ = MP_CLOSED;
michael@0 202 UpdateRtcpMuxState(info);
michael@0 203 MOZ_CRASH(); // TODO: Remove once we have enough field experience to
michael@0 204 // know it doesn't happen. bug 798797. Note that the
michael@0 205 // code after this never executes.
michael@0 206 return res;
michael@0 207 }
michael@0 208
michael@0 209 // Slice and dice as per RFC 5764 S 4.2
michael@0 210 unsigned char client_write_key[SRTP_TOTAL_KEY_LENGTH];
michael@0 211 unsigned char server_write_key[SRTP_TOTAL_KEY_LENGTH];
michael@0 212 int offset = 0;
michael@0 213 memcpy(client_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
michael@0 214 offset += SRTP_MASTER_KEY_LENGTH;
michael@0 215 memcpy(server_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
michael@0 216 offset += SRTP_MASTER_KEY_LENGTH;
michael@0 217 memcpy(client_write_key + SRTP_MASTER_KEY_LENGTH,
michael@0 218 srtp_block + offset, SRTP_MASTER_SALT_LENGTH);
michael@0 219 offset += SRTP_MASTER_SALT_LENGTH;
michael@0 220 memcpy(server_write_key + SRTP_MASTER_KEY_LENGTH,
michael@0 221 srtp_block + offset, SRTP_MASTER_SALT_LENGTH);
michael@0 222 offset += SRTP_MASTER_SALT_LENGTH;
michael@0 223 MOZ_ASSERT(offset == sizeof(srtp_block));
michael@0 224
michael@0 225 unsigned char *write_key;
michael@0 226 unsigned char *read_key;
michael@0 227
michael@0 228 if (dtls->role() == TransportLayerDtls::CLIENT) {
michael@0 229 write_key = client_write_key;
michael@0 230 read_key = server_write_key;
michael@0 231 } else {
michael@0 232 write_key = server_write_key;
michael@0 233 read_key = client_write_key;
michael@0 234 }
michael@0 235
michael@0 236 MOZ_ASSERT(!info.send_srtp_ && !info.recv_srtp_);
michael@0 237 info.send_srtp_ = SrtpFlow::Create(cipher_suite, false, write_key,
michael@0 238 SRTP_TOTAL_KEY_LENGTH);
michael@0 239 info.recv_srtp_ = SrtpFlow::Create(cipher_suite, true, read_key,
michael@0 240 SRTP_TOTAL_KEY_LENGTH);
michael@0 241 if (!info.send_srtp_ || !info.recv_srtp_) {
michael@0 242 MOZ_MTLOG(ML_ERROR, "Couldn't create SRTP flow for "
michael@0 243 << ToString(info.type_));
michael@0 244 info.state_ = MP_CLOSED;
michael@0 245 UpdateRtcpMuxState(info);
michael@0 246 return NS_ERROR_FAILURE;
michael@0 247 }
michael@0 248
michael@0 249 MOZ_MTLOG(ML_INFO, "Listening for " << ToString(info.type_)
michael@0 250 << " packets received on " <<
michael@0 251 static_cast<void *>(dtls->downward()));
michael@0 252
michael@0 253 switch (info.type_) {
michael@0 254 case RTP:
michael@0 255 dtls->downward()->SignalPacketReceived.connect(
michael@0 256 this,
michael@0 257 &MediaPipeline::RtpPacketReceived);
michael@0 258 break;
michael@0 259 case RTCP:
michael@0 260 dtls->downward()->SignalPacketReceived.connect(
michael@0 261 this,
michael@0 262 &MediaPipeline::RtcpPacketReceived);
michael@0 263 break;
michael@0 264 case MUX:
michael@0 265 dtls->downward()->SignalPacketReceived.connect(
michael@0 266 this,
michael@0 267 &MediaPipeline::PacketReceived);
michael@0 268 break;
michael@0 269 default:
michael@0 270 MOZ_CRASH();
michael@0 271 }
michael@0 272
michael@0 273 info.state_ = MP_OPEN;
michael@0 274 UpdateRtcpMuxState(info);
michael@0 275 return NS_OK;
michael@0 276 }
michael@0 277
michael@0 278 nsresult MediaPipeline::TransportFailed_s(TransportInfo &info) {
michael@0 279 ASSERT_ON_THREAD(sts_thread_);
michael@0 280
michael@0 281 info.state_ = MP_CLOSED;
michael@0 282 UpdateRtcpMuxState(info);
michael@0 283
michael@0 284 MOZ_MTLOG(ML_INFO, "Transport closed for flow " << ToString(info.type_));
michael@0 285
michael@0 286 NS_WARNING(
michael@0 287 "MediaPipeline Transport failed. This is not properly cleaned up yet");
michael@0 288
michael@0 289 // TODO(ekr@rtfm.com): SECURITY: Figure out how to clean up if the
michael@0 290 // connection was good and now it is bad.
michael@0 291 // TODO(ekr@rtfm.com): Report up so that the PC knows we
michael@0 292 // have experienced an error.
michael@0 293
michael@0 294 return NS_OK;
michael@0 295 }
michael@0 296
michael@0 297 void MediaPipeline::UpdateRtcpMuxState(TransportInfo &info) {
michael@0 298 if (info.type_ == MUX) {
michael@0 299 if (info.transport_ == rtcp_.transport_) {
michael@0 300 rtcp_.state_ = info.state_;
michael@0 301 if (!rtcp_.send_srtp_) {
michael@0 302 rtcp_.send_srtp_ = info.send_srtp_;
michael@0 303 rtcp_.recv_srtp_ = info.recv_srtp_;
michael@0 304 }
michael@0 305 } else if (possible_bundle_rtcp_ &&
michael@0 306 info.transport_ == possible_bundle_rtcp_->transport_) {
michael@0 307 possible_bundle_rtcp_->state_ = info.state_;
michael@0 308 if (!possible_bundle_rtcp_->send_srtp_) {
michael@0 309 possible_bundle_rtcp_->send_srtp_ = info.send_srtp_;
michael@0 310 possible_bundle_rtcp_->recv_srtp_ = info.recv_srtp_;
michael@0 311 }
michael@0 312 }
michael@0 313 }
michael@0 314 }
michael@0 315
michael@0 316 nsresult MediaPipeline::SendPacket(TransportFlow *flow, const void *data,
michael@0 317 int len) {
michael@0 318 ASSERT_ON_THREAD(sts_thread_);
michael@0 319
michael@0 320 // Note that we bypass the DTLS layer here
michael@0 321 TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
michael@0 322 flow->GetLayer(TransportLayerDtls::ID()));
michael@0 323 MOZ_ASSERT(dtls);
michael@0 324
michael@0 325 TransportResult res = dtls->downward()->
michael@0 326 SendPacket(static_cast<const unsigned char *>(data), len);
michael@0 327
michael@0 328 if (res != len) {
michael@0 329 // Ignore blocking indications
michael@0 330 if (res == TE_WOULDBLOCK)
michael@0 331 return NS_OK;
michael@0 332
michael@0 333 MOZ_MTLOG(ML_ERROR, "Failed write on stream");
michael@0 334 return NS_BASE_STREAM_CLOSED;
michael@0 335 }
michael@0 336
michael@0 337 return NS_OK;
michael@0 338 }
michael@0 339
michael@0 340 void MediaPipeline::increment_rtp_packets_sent(int32_t bytes) {
michael@0 341 ++rtp_packets_sent_;
michael@0 342 rtp_bytes_sent_ += bytes;
michael@0 343
michael@0 344 if (!(rtp_packets_sent_ % 100)) {
michael@0 345 MOZ_MTLOG(ML_INFO, "RTP sent packet count for " << description_
michael@0 346 << " Pipeline " << static_cast<void *>(this)
michael@0 347 << " Flow : " << static_cast<void *>(rtp_.transport_)
michael@0 348 << ": " << rtp_packets_sent_
michael@0 349 << " (" << rtp_bytes_sent_ << " bytes)");
michael@0 350 }
michael@0 351 }
michael@0 352
michael@0 353 void MediaPipeline::increment_rtcp_packets_sent() {
michael@0 354 ++rtcp_packets_sent_;
michael@0 355 if (!(rtcp_packets_sent_ % 100)) {
michael@0 356 MOZ_MTLOG(ML_INFO, "RTCP sent packet count for " << description_
michael@0 357 << " Pipeline " << static_cast<void *>(this)
michael@0 358 << " Flow : " << static_cast<void *>(rtcp_.transport_)
michael@0 359 << ": " << rtcp_packets_sent_);
michael@0 360 }
michael@0 361 }
michael@0 362
michael@0 363 void MediaPipeline::increment_rtp_packets_received(int32_t bytes) {
michael@0 364 ++rtp_packets_received_;
michael@0 365 rtp_bytes_received_ += bytes;
michael@0 366 if (!(rtp_packets_received_ % 100)) {
michael@0 367 MOZ_MTLOG(ML_INFO, "RTP received packet count for " << description_
michael@0 368 << " Pipeline " << static_cast<void *>(this)
michael@0 369 << " Flow : " << static_cast<void *>(rtp_.transport_)
michael@0 370 << ": " << rtp_packets_received_
michael@0 371 << " (" << rtp_bytes_received_ << " bytes)");
michael@0 372 }
michael@0 373 }
michael@0 374
michael@0 375 void MediaPipeline::increment_rtcp_packets_received() {
michael@0 376 ++rtcp_packets_received_;
michael@0 377 if (!(rtcp_packets_received_ % 100)) {
michael@0 378 MOZ_MTLOG(ML_INFO, "RTCP received packet count for " << description_
michael@0 379 << " Pipeline " << static_cast<void *>(this)
michael@0 380 << " Flow : " << static_cast<void *>(rtcp_.transport_)
michael@0 381 << ": " << rtcp_packets_received_);
michael@0 382 }
michael@0 383 }
michael@0 384
michael@0 385 void MediaPipeline::RtpPacketReceived(TransportLayer *layer,
michael@0 386 const unsigned char *data,
michael@0 387 size_t len) {
michael@0 388 if (!transport_->pipeline()) {
michael@0 389 MOZ_MTLOG(ML_ERROR, "Discarding incoming packet; transport disconnected");
michael@0 390 return;
michael@0 391 }
michael@0 392
michael@0 393 if (!conduit_) {
michael@0 394 MOZ_MTLOG(ML_DEBUG, "Discarding incoming packet; media disconnected");
michael@0 395 return;
michael@0 396 }
michael@0 397
michael@0 398 TransportInfo* info = &rtp_;
michael@0 399
michael@0 400 if (possible_bundle_rtp_ &&
michael@0 401 possible_bundle_rtp_->transport_->Contains(layer)) {
michael@0 402 // Received this on our possible bundle transport. Override info.
michael@0 403 info = possible_bundle_rtp_;
michael@0 404 }
michael@0 405
michael@0 406 // TODO(bcampen@mozilla.com): Can either of these actually happen? If not,
michael@0 407 // the info variable can be removed, and this function gets simpler.
michael@0 408 if (info->state_ != MP_OPEN) {
michael@0 409 MOZ_MTLOG(ML_ERROR, "Discarding incoming packet; pipeline not open");
michael@0 410 return;
michael@0 411 }
michael@0 412
michael@0 413 if (info->transport_->state() != TransportLayer::TS_OPEN) {
michael@0 414 MOZ_MTLOG(ML_ERROR, "Discarding incoming packet; transport not open");
michael@0 415 return;
michael@0 416 }
michael@0 417
michael@0 418 // This should never happen.
michael@0 419 MOZ_ASSERT(info->recv_srtp_);
michael@0 420
michael@0 421 if (direction_ == TRANSMIT) {
michael@0 422 return;
michael@0 423 }
michael@0 424
michael@0 425 if (possible_bundle_rtp_ && (info == &rtp_)) {
michael@0 426 // We were not sure we would be using rtp_ or possible_bundle_rtp_, but we
michael@0 427 // have just received traffic that clears this up.
michael@0 428 // Don't let our filter prevent us from noticing this, if the filter is
michael@0 429 // incomplete (ie; no SSRCs in remote SDP, or no remote SDP at all).
michael@0 430 SetUsingBundle_s(false);
michael@0 431 MOZ_MTLOG(ML_INFO, "Ruled out the possibility that we're receiving bundle "
michael@0 432 "for " << description_);
michael@0 433 // TODO(bcampen@mozilla.com): Might be nice to detect when every
michael@0 434 // MediaPipeline but the master has determined that it isn't doing bundle,
michael@0 435 // since that means the master isn't doing bundle either. We could maybe
michael@0 436 // do this by putting some refcounted dummy variable in the filters, and
michael@0 437 // checking the value of the refcount. It is not clear whether this is
michael@0 438 // going to be useful in practice.
michael@0 439 }
michael@0 440
michael@0 441 if (!len) {
michael@0 442 return;
michael@0 443 }
michael@0 444
michael@0 445 // Filter out everything but RTP/RTCP
michael@0 446 if (data[0] < 128 || data[0] > 191) {
michael@0 447 return;
michael@0 448 }
michael@0 449
michael@0 450 if (filter_) {
michael@0 451 webrtc::RTPHeader header;
michael@0 452 if (!rtp_parser_->Parse(data, len, &header) ||
michael@0 453 !filter_->Filter(header)) {
michael@0 454 return;
michael@0 455 }
michael@0 456 }
michael@0 457
michael@0 458 if (possible_bundle_rtp_) {
michael@0 459 // Just got traffic that passed our filter on the potential bundle
michael@0 460 // transport. Must be doing bundle.
michael@0 461 SetUsingBundle_s(true);
michael@0 462 MOZ_MTLOG(ML_INFO, "Confirmed the possibility that we're receiving bundle");
michael@0 463 }
michael@0 464
michael@0 465 // Everything is decided now; just use rtp_
michael@0 466 MOZ_ASSERT(!possible_bundle_rtp_);
michael@0 467 MOZ_ASSERT(!possible_bundle_rtcp_);
michael@0 468
michael@0 469 // Make a copy rather than cast away constness
michael@0 470 ScopedDeletePtr<unsigned char> inner_data(
michael@0 471 new unsigned char[len]);
michael@0 472 memcpy(inner_data, data, len);
michael@0 473 int out_len = 0;
michael@0 474 nsresult res = rtp_.recv_srtp_->UnprotectRtp(inner_data,
michael@0 475 len, len, &out_len);
michael@0 476 if (!NS_SUCCEEDED(res)) {
michael@0 477 char tmp[16];
michael@0 478
michael@0 479 PR_snprintf(tmp, sizeof(tmp), "%.2x %.2x %.2x %.2x",
michael@0 480 inner_data[0],
michael@0 481 inner_data[1],
michael@0 482 inner_data[2],
michael@0 483 inner_data[3]);
michael@0 484
michael@0 485 MOZ_MTLOG(ML_NOTICE, "Error unprotecting RTP in " << description_
michael@0 486 << "len= " << len << "[" << tmp << "...]");
michael@0 487
michael@0 488 return;
michael@0 489 }
michael@0 490 increment_rtp_packets_received(out_len);
michael@0 491
michael@0 492 (void)conduit_->ReceivedRTPPacket(inner_data, out_len); // Ignore error codes
michael@0 493 }
michael@0 494
michael@0 495 void MediaPipeline::RtcpPacketReceived(TransportLayer *layer,
michael@0 496 const unsigned char *data,
michael@0 497 size_t len) {
michael@0 498 if (!transport_->pipeline()) {
michael@0 499 MOZ_MTLOG(ML_DEBUG, "Discarding incoming packet; transport disconnected");
michael@0 500 return;
michael@0 501 }
michael@0 502
michael@0 503 if (!conduit_) {
michael@0 504 MOZ_MTLOG(ML_DEBUG, "Discarding incoming packet; media disconnected");
michael@0 505 return;
michael@0 506 }
michael@0 507
michael@0 508 TransportInfo* info = &rtcp_;
michael@0 509 if (possible_bundle_rtcp_ &&
michael@0 510 possible_bundle_rtcp_->transport_->Contains(layer)) {
michael@0 511 info = possible_bundle_rtcp_;
michael@0 512 }
michael@0 513
michael@0 514 if (info->state_ != MP_OPEN) {
michael@0 515 MOZ_MTLOG(ML_DEBUG, "Discarding incoming packet; pipeline not open");
michael@0 516 return;
michael@0 517 }
michael@0 518
michael@0 519 if (info->transport_->state() != TransportLayer::TS_OPEN) {
michael@0 520 MOZ_MTLOG(ML_ERROR, "Discarding incoming packet; transport not open");
michael@0 521 return;
michael@0 522 }
michael@0 523
michael@0 524 if (possible_bundle_rtp_ && (info == &rtcp_)) {
michael@0 525 // We have offered bundle, and received our first packet on a non-bundle
michael@0 526 // address. We are definitely not using the bundle address.
michael@0 527 SetUsingBundle_s(false);
michael@0 528 }
michael@0 529
michael@0 530 if (!len) {
michael@0 531 return;
michael@0 532 }
michael@0 533
michael@0 534 // Filter out everything but RTP/RTCP
michael@0 535 if (data[0] < 128 || data[0] > 191) {
michael@0 536 return;
michael@0 537 }
michael@0 538
michael@0 539 MediaPipelineFilter::Result filter_result = MediaPipelineFilter::PASS;
michael@0 540 if (filter_) {
michael@0 541 filter_result = filter_->FilterRTCP(data, len);
michael@0 542 if (filter_result == MediaPipelineFilter::FAIL) {
michael@0 543 return;
michael@0 544 }
michael@0 545 }
michael@0 546
michael@0 547 if (filter_result == MediaPipelineFilter::PASS && possible_bundle_rtp_) {
michael@0 548 // Just got traffic that passed our filter on the potential bundle
michael@0 549 // transport. Must be doing bundle.
michael@0 550 SetUsingBundle_s(true);
michael@0 551 }
michael@0 552
michael@0 553 // We continue using info here, since it is possible that the filter did not
michael@0 554 // support the payload type (ie; returned MediaPipelineFilter::UNSUPPORTED).
michael@0 555 // In this case, we just let it pass, and hope the webrtc.org code does
michael@0 556 // something sane.
michael@0 557 increment_rtcp_packets_received();
michael@0 558
michael@0 559 MOZ_ASSERT(info->recv_srtp_); // This should never happen
michael@0 560
michael@0 561 // Make a copy rather than cast away constness
michael@0 562 ScopedDeletePtr<unsigned char> inner_data(
michael@0 563 new unsigned char[len]);
michael@0 564 memcpy(inner_data, data, len);
michael@0 565 int out_len;
michael@0 566
michael@0 567 nsresult res = info->recv_srtp_->UnprotectRtcp(inner_data,
michael@0 568 len,
michael@0 569 len,
michael@0 570 &out_len);
michael@0 571
michael@0 572 if (!NS_SUCCEEDED(res))
michael@0 573 return;
michael@0 574
michael@0 575 (void)conduit_->ReceivedRTCPPacket(inner_data, out_len); // Ignore error codes
michael@0 576 }
michael@0 577
michael@0 578 bool MediaPipeline::IsRtp(const unsigned char *data, size_t len) {
michael@0 579 if (len < 2)
michael@0 580 return false;
michael@0 581
michael@0 582 // Check if this is a RTCP packet. Logic based on the types listed in
michael@0 583 // media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc
michael@0 584
michael@0 585 // Anything outside this range is RTP.
michael@0 586 if ((data[1] < 192) || (data[1] > 207))
michael@0 587 return true;
michael@0 588
michael@0 589 if (data[1] == 192) // FIR
michael@0 590 return false;
michael@0 591
michael@0 592 if (data[1] == 193) // NACK, but could also be RTP. This makes us sad
michael@0 593 return true; // but it's how webrtc.org behaves.
michael@0 594
michael@0 595 if (data[1] == 194)
michael@0 596 return true;
michael@0 597
michael@0 598 if (data[1] == 195) // IJ.
michael@0 599 return false;
michael@0 600
michael@0 601 if ((data[1] > 195) && (data[1] < 200)) // the > 195 is redundant
michael@0 602 return true;
michael@0 603
michael@0 604 if ((data[1] >= 200) && (data[1] <= 207)) // SR, RR, SDES, BYE,
michael@0 605 return false; // APP, RTPFB, PSFB, XR
michael@0 606
michael@0 607 MOZ_ASSERT(false); // Not reached, belt and suspenders.
michael@0 608 return true;
michael@0 609 }
michael@0 610
michael@0 611 void MediaPipeline::PacketReceived(TransportLayer *layer,
michael@0 612 const unsigned char *data,
michael@0 613 size_t len) {
michael@0 614 if (!transport_->pipeline()) {
michael@0 615 MOZ_MTLOG(ML_DEBUG, "Discarding incoming packet; transport disconnected");
michael@0 616 return;
michael@0 617 }
michael@0 618
michael@0 619 if (IsRtp(data, len)) {
michael@0 620 RtpPacketReceived(layer, data, len);
michael@0 621 } else {
michael@0 622 RtcpPacketReceived(layer, data, len);
michael@0 623 }
michael@0 624 }
michael@0 625
michael@0 626 nsresult MediaPipelineTransmit::Init() {
michael@0 627 char track_id_string[11];
michael@0 628 ASSERT_ON_THREAD(main_thread_);
michael@0 629
michael@0 630 // We can replace this when we are allowed to do streams or std::to_string
michael@0 631 PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id_);
michael@0 632
michael@0 633 description_ = pc_ + "| ";
michael@0 634 description_ += conduit_->type() == MediaSessionConduit::AUDIO ?
michael@0 635 "Transmit audio[" : "Transmit video[";
michael@0 636 description_ += track_id_string;
michael@0 637 description_ += "]";
michael@0 638
michael@0 639 // TODO(ekr@rtfm.com): Check for errors
michael@0 640 MOZ_MTLOG(ML_DEBUG, "Attaching pipeline to stream "
michael@0 641 << static_cast<void *>(stream_) << " conduit type=" <<
michael@0 642 (conduit_->type() == MediaSessionConduit::AUDIO ?"audio":"video"));
michael@0 643
michael@0 644 stream_->AddListener(listener_);
michael@0 645
michael@0 646 // Is this a gUM mediastream? If so, also register the Listener directly with
michael@0 647 // the SourceMediaStream that's attached to the TrackUnion so we can get direct
michael@0 648 // unqueued (and not resampled) data
michael@0 649 if (domstream_->AddDirectListener(listener_)) {
michael@0 650 listener_->direct_connect_ = true;
michael@0 651 }
michael@0 652
michael@0 653 return MediaPipeline::Init();
michael@0 654 }
michael@0 655
michael@0 656 nsresult MediaPipelineTransmit::TransportReady_s(TransportInfo &info) {
michael@0 657 ASSERT_ON_THREAD(sts_thread_);
michael@0 658 // Call base ready function.
michael@0 659 MediaPipeline::TransportReady_s(info);
michael@0 660
michael@0 661 // Should not be set for a transmitter
michael@0 662 MOZ_ASSERT(!possible_bundle_rtp_);
michael@0 663 if (&info == &rtp_) {
michael@0 664 // TODO(ekr@rtfm.com): Move onto MSG thread.
michael@0 665 listener_->SetActive(true);
michael@0 666 }
michael@0 667
michael@0 668 return NS_OK;
michael@0 669 }
michael@0 670
michael@0 671 void MediaPipeline::DisconnectTransport_s(TransportInfo &info) {
michael@0 672 MOZ_ASSERT(info.transport_);
michael@0 673 ASSERT_ON_THREAD(sts_thread_);
michael@0 674
michael@0 675 info.transport_->SignalStateChange.disconnect(this);
michael@0 676 // We do this even if we're a transmitter, since we are still possibly
michael@0 677 // registered to receive RTCP.
michael@0 678 TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
michael@0 679 info.transport_->GetLayer(TransportLayerDtls::ID()));
michael@0 680 MOZ_ASSERT(dtls); // DTLS is mandatory
michael@0 681 MOZ_ASSERT(dtls->downward());
michael@0 682 dtls->downward()->SignalPacketReceived.disconnect(this);
michael@0 683 }
michael@0 684
michael@0 685 nsresult MediaPipeline::ConnectTransport_s(TransportInfo &info) {
michael@0 686 MOZ_ASSERT(info.transport_);
michael@0 687 ASSERT_ON_THREAD(sts_thread_);
michael@0 688
michael@0 689 // Look to see if the transport is ready
michael@0 690 if (info.transport_->state() == TransportLayer::TS_OPEN) {
michael@0 691 nsresult res = TransportReady_s(info);
michael@0 692 if (NS_FAILED(res)) {
michael@0 693 MOZ_MTLOG(ML_ERROR, "Error calling TransportReady(); res="
michael@0 694 << static_cast<uint32_t>(res) << " in " << __FUNCTION__);
michael@0 695 return res;
michael@0 696 }
michael@0 697 } else if (info.transport_->state() == TransportLayer::TS_ERROR) {
michael@0 698 MOZ_MTLOG(ML_ERROR, ToString(info.type_)
michael@0 699 << "transport is already in error state");
michael@0 700 TransportFailed_s(info);
michael@0 701 return NS_ERROR_FAILURE;
michael@0 702 }
michael@0 703
michael@0 704 info.transport_->SignalStateChange.connect(this,
michael@0 705 &MediaPipeline::StateChange);
michael@0 706
michael@0 707 return NS_OK;
michael@0 708 }
michael@0 709
michael@0 710 MediaPipeline::TransportInfo* MediaPipeline::GetTransportInfo_s(
michael@0 711 TransportFlow *flow) {
michael@0 712 ASSERT_ON_THREAD(sts_thread_);
michael@0 713 if (flow == rtp_.transport_) {
michael@0 714 return &rtp_;
michael@0 715 }
michael@0 716
michael@0 717 if (flow == rtcp_.transport_) {
michael@0 718 return &rtcp_;
michael@0 719 }
michael@0 720
michael@0 721 if (possible_bundle_rtp_) {
michael@0 722 if (flow == possible_bundle_rtp_->transport_) {
michael@0 723 return possible_bundle_rtp_;
michael@0 724 }
michael@0 725
michael@0 726 if (flow == possible_bundle_rtcp_->transport_) {
michael@0 727 return possible_bundle_rtcp_;
michael@0 728 }
michael@0 729 }
michael@0 730
michael@0 731 return nullptr;
michael@0 732 }
michael@0 733
michael@0 734 void MediaPipeline::SetUsingBundle_s(bool decision) {
michael@0 735 ASSERT_ON_THREAD(sts_thread_);
michael@0 736 // Note: This can be called either because of events on the STS thread, or
michael@0 737 // by events on the main thread (ie; receiving a remote description). It is
michael@0 738 // best to be careful of races here, so don't assume that transports are open.
michael@0 739 if (!possible_bundle_rtp_) {
michael@0 740 if (!decision) {
michael@0 741 // This can happen on the master pipeline.
michael@0 742 filter_ = nullptr;
michael@0 743 }
michael@0 744 return;
michael@0 745 }
michael@0 746
michael@0 747 if (direction_ == RECEIVE) {
michael@0 748 if (decision) {
michael@0 749 MOZ_MTLOG(ML_INFO, "Non-master pipeline confirmed bundle for "
michael@0 750 << description_);
michael@0 751 // We're doing bundle. Release the unused flows, and copy the ones we
michael@0 752 // are using into the less wishy-washy members.
michael@0 753 DisconnectTransport_s(rtp_);
michael@0 754 DisconnectTransport_s(rtcp_);
michael@0 755 rtp_ = *possible_bundle_rtp_;
michael@0 756 rtcp_ = *possible_bundle_rtcp_;
michael@0 757 } else {
michael@0 758 MOZ_MTLOG(ML_INFO, "Non-master pipeline confirmed no bundle for "
michael@0 759 << description_);
michael@0 760 // We are not doing bundle
michael@0 761 DisconnectTransport_s(*possible_bundle_rtp_);
michael@0 762 DisconnectTransport_s(*possible_bundle_rtcp_);
michael@0 763 filter_ = nullptr;
michael@0 764 }
michael@0 765
michael@0 766 // We are no longer in an ambiguous state.
michael@0 767 possible_bundle_rtp_ = nullptr;
michael@0 768 possible_bundle_rtcp_ = nullptr;
michael@0 769 }
michael@0 770 }
michael@0 771
michael@0 772 MediaPipelineFilter* MediaPipeline::UpdateFilterFromRemoteDescription_s(
michael@0 773 nsAutoPtr<MediaPipelineFilter> filter) {
michael@0 774 ASSERT_ON_THREAD(sts_thread_);
michael@0 775 // This is only supposed to relax the filter. Relaxing a missing filter is
michael@0 776 // not possible.
michael@0 777 MOZ_ASSERT(filter_);
michael@0 778
michael@0 779 if (!filter) {
michael@0 780 filter_ = nullptr;
michael@0 781 } else {
michael@0 782 filter_->IncorporateRemoteDescription(*filter);
michael@0 783 }
michael@0 784
michael@0 785 return filter_.get();
michael@0 786 }
michael@0 787
michael@0 788 nsresult MediaPipeline::PipelineTransport::SendRtpPacket(
michael@0 789 const void *data, int len) {
michael@0 790
michael@0 791 nsAutoPtr<DataBuffer> buf(new DataBuffer(static_cast<const uint8_t *>(data),
michael@0 792 len));
michael@0 793
michael@0 794 RUN_ON_THREAD(sts_thread_,
michael@0 795 WrapRunnable(
michael@0 796 RefPtr<MediaPipeline::PipelineTransport>(this),
michael@0 797 &MediaPipeline::PipelineTransport::SendRtpPacket_s,
michael@0 798 buf),
michael@0 799 NS_DISPATCH_NORMAL);
michael@0 800
michael@0 801 return NS_OK;
michael@0 802 }
michael@0 803
michael@0 804 nsresult MediaPipeline::PipelineTransport::SendRtpPacket_s(
michael@0 805 nsAutoPtr<DataBuffer> data) {
michael@0 806 ASSERT_ON_THREAD(sts_thread_);
michael@0 807 if (!pipeline_)
michael@0 808 return NS_OK; // Detached
michael@0 809
michael@0 810 if (!pipeline_->rtp_.send_srtp_) {
michael@0 811 MOZ_MTLOG(ML_DEBUG, "Couldn't write RTP packet; SRTP not set up yet");
michael@0 812 return NS_OK;
michael@0 813 }
michael@0 814
michael@0 815 MOZ_ASSERT(pipeline_->rtp_.transport_);
michael@0 816 NS_ENSURE_TRUE(pipeline_->rtp_.transport_, NS_ERROR_NULL_POINTER);
michael@0 817
michael@0 818 // libsrtp enciphers in place, so we need a new, big enough
michael@0 819 // buffer.
michael@0 820 // XXX. allocates and deletes one buffer per packet sent.
michael@0 821 // Bug 822129
michael@0 822 int max_len = data->len() + SRTP_MAX_EXPANSION;
michael@0 823 ScopedDeletePtr<unsigned char> inner_data(
michael@0 824 new unsigned char[max_len]);
michael@0 825 memcpy(inner_data, data->data(), data->len());
michael@0 826
michael@0 827 int out_len;
michael@0 828 nsresult res = pipeline_->rtp_.send_srtp_->ProtectRtp(inner_data,
michael@0 829 data->len(),
michael@0 830 max_len,
michael@0 831 &out_len);
michael@0 832 if (!NS_SUCCEEDED(res))
michael@0 833 return res;
michael@0 834
michael@0 835 pipeline_->increment_rtp_packets_sent(out_len);
michael@0 836 return pipeline_->SendPacket(pipeline_->rtp_.transport_, inner_data,
michael@0 837 out_len);
michael@0 838 }
michael@0 839
michael@0 840 nsresult MediaPipeline::PipelineTransport::SendRtcpPacket(
michael@0 841 const void *data, int len) {
michael@0 842
michael@0 843 nsAutoPtr<DataBuffer> buf(new DataBuffer(static_cast<const uint8_t *>(data),
michael@0 844 len));
michael@0 845
michael@0 846 RUN_ON_THREAD(sts_thread_,
michael@0 847 WrapRunnable(
michael@0 848 RefPtr<MediaPipeline::PipelineTransport>(this),
michael@0 849 &MediaPipeline::PipelineTransport::SendRtcpPacket_s,
michael@0 850 buf),
michael@0 851 NS_DISPATCH_NORMAL);
michael@0 852
michael@0 853 return NS_OK;
michael@0 854 }
michael@0 855
michael@0 856 nsresult MediaPipeline::PipelineTransport::SendRtcpPacket_s(
michael@0 857 nsAutoPtr<DataBuffer> data) {
michael@0 858 ASSERT_ON_THREAD(sts_thread_);
michael@0 859 if (!pipeline_)
michael@0 860 return NS_OK; // Detached
michael@0 861
michael@0 862 if (!pipeline_->rtcp_.send_srtp_) {
michael@0 863 MOZ_MTLOG(ML_DEBUG, "Couldn't write RTCP packet; SRTCP not set up yet");
michael@0 864 return NS_OK;
michael@0 865 }
michael@0 866
michael@0 867 MOZ_ASSERT(pipeline_->rtcp_.transport_);
michael@0 868 NS_ENSURE_TRUE(pipeline_->rtcp_.transport_, NS_ERROR_NULL_POINTER);
michael@0 869
michael@0 870 // libsrtp enciphers in place, so we need a new, big enough
michael@0 871 // buffer.
michael@0 872 // XXX. allocates and deletes one buffer per packet sent.
michael@0 873 // Bug 822129.
michael@0 874 int max_len = data->len() + SRTP_MAX_EXPANSION;
michael@0 875 ScopedDeletePtr<unsigned char> inner_data(
michael@0 876 new unsigned char[max_len]);
michael@0 877 memcpy(inner_data, data->data(), data->len());
michael@0 878
michael@0 879 int out_len;
michael@0 880 nsresult res = pipeline_->rtcp_.send_srtp_->ProtectRtcp(inner_data,
michael@0 881 data->len(),
michael@0 882 max_len,
michael@0 883 &out_len);
michael@0 884
michael@0 885 if (!NS_SUCCEEDED(res))
michael@0 886 return res;
michael@0 887
michael@0 888 pipeline_->increment_rtcp_packets_sent();
michael@0 889 return pipeline_->SendPacket(pipeline_->rtcp_.transport_, inner_data,
michael@0 890 out_len);
michael@0 891 }
michael@0 892
michael@0 893 // Called if we're attached with AddDirectListener()
michael@0 894 void MediaPipelineTransmit::PipelineListener::
michael@0 895 NotifyRealtimeData(MediaStreamGraph* graph, TrackID tid,
michael@0 896 TrackRate rate,
michael@0 897 TrackTicks offset,
michael@0 898 uint32_t events,
michael@0 899 const MediaSegment& media) {
michael@0 900 MOZ_MTLOG(ML_DEBUG, "MediaPipeline::NotifyRealtimeData()");
michael@0 901
michael@0 902 NewData(graph, tid, rate, offset, events, media);
michael@0 903 }
michael@0 904
michael@0 905 void MediaPipelineTransmit::PipelineListener::
michael@0 906 NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid,
michael@0 907 TrackRate rate,
michael@0 908 TrackTicks offset,
michael@0 909 uint32_t events,
michael@0 910 const MediaSegment& queued_media) {
michael@0 911 MOZ_MTLOG(ML_DEBUG, "MediaPipeline::NotifyQueuedTrackChanges()");
michael@0 912
michael@0 913 // ignore non-direct data if we're also getting direct data
michael@0 914 if (!direct_connect_) {
michael@0 915 NewData(graph, tid, rate, offset, events, queued_media);
michael@0 916 }
michael@0 917 }
michael@0 918
michael@0 919 void MediaPipelineTransmit::PipelineListener::
michael@0 920 NewData(MediaStreamGraph* graph, TrackID tid,
michael@0 921 TrackRate rate,
michael@0 922 TrackTicks offset,
michael@0 923 uint32_t events,
michael@0 924 const MediaSegment& media) {
michael@0 925 if (!active_) {
michael@0 926 MOZ_MTLOG(ML_DEBUG, "Discarding packets because transport not ready");
michael@0 927 return;
michael@0 928 }
michael@0 929
michael@0 930 // TODO(ekr@rtfm.com): For now assume that we have only one
michael@0 931 // track type and it's destined for us
michael@0 932 // See bug 784517
michael@0 933 if (media.GetType() == MediaSegment::AUDIO) {
michael@0 934 if (conduit_->type() != MediaSessionConduit::AUDIO) {
michael@0 935 // Ignore data in case we have a muxed stream
michael@0 936 return;
michael@0 937 }
michael@0 938 AudioSegment* audio = const_cast<AudioSegment *>(
michael@0 939 static_cast<const AudioSegment *>(&media));
michael@0 940
michael@0 941 AudioSegment::ChunkIterator iter(*audio);
michael@0 942 while(!iter.IsEnded()) {
michael@0 943 ProcessAudioChunk(static_cast<AudioSessionConduit*>(conduit_.get()),
michael@0 944 rate, *iter);
michael@0 945 iter.Next();
michael@0 946 }
michael@0 947 } else if (media.GetType() == MediaSegment::VIDEO) {
michael@0 948 #ifdef MOZILLA_INTERNAL_API
michael@0 949 if (conduit_->type() != MediaSessionConduit::VIDEO) {
michael@0 950 // Ignore data in case we have a muxed stream
michael@0 951 return;
michael@0 952 }
michael@0 953 VideoSegment* video = const_cast<VideoSegment *>(
michael@0 954 static_cast<const VideoSegment *>(&media));
michael@0 955
michael@0 956 VideoSegment::ChunkIterator iter(*video);
michael@0 957 while(!iter.IsEnded()) {
michael@0 958 ProcessVideoChunk(static_cast<VideoSessionConduit*>(conduit_.get()),
michael@0 959 rate, *iter);
michael@0 960 iter.Next();
michael@0 961 }
michael@0 962 #endif
michael@0 963 } else {
michael@0 964 // Ignore
michael@0 965 }
michael@0 966 }
michael@0 967
michael@0 968 void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk(
michael@0 969 AudioSessionConduit *conduit,
michael@0 970 TrackRate rate,
michael@0 971 AudioChunk& chunk) {
michael@0 972 // TODO(ekr@rtfm.com): Do more than one channel
michael@0 973 nsAutoArrayPtr<int16_t> samples(new int16_t[chunk.mDuration]);
michael@0 974
michael@0 975 if (chunk.mBuffer) {
michael@0 976 switch (chunk.mBufferFormat) {
michael@0 977 case AUDIO_FORMAT_FLOAT32:
michael@0 978 {
michael@0 979 const float* buf = static_cast<const float *>(chunk.mChannelData[0]);
michael@0 980 ConvertAudioSamplesWithScale(buf, static_cast<int16_t*>(samples),
michael@0 981 chunk.mDuration, chunk.mVolume);
michael@0 982 }
michael@0 983 break;
michael@0 984 case AUDIO_FORMAT_S16:
michael@0 985 {
michael@0 986 const short* buf = static_cast<const short *>(chunk.mChannelData[0]);
michael@0 987 ConvertAudioSamplesWithScale(buf, samples, chunk.mDuration, chunk.mVolume);
michael@0 988 }
michael@0 989 break;
michael@0 990 case AUDIO_FORMAT_SILENCE:
michael@0 991 memset(samples, 0, chunk.mDuration * sizeof(samples[0]));
michael@0 992 break;
michael@0 993 default:
michael@0 994 MOZ_ASSERT(PR_FALSE);
michael@0 995 return;
michael@0 996 break;
michael@0 997 }
michael@0 998 } else {
michael@0 999 // This means silence.
michael@0 1000 memset(samples, 0, chunk.mDuration * sizeof(samples[0]));
michael@0 1001 }
michael@0 1002
michael@0 1003 MOZ_ASSERT(!(rate%100)); // rate should be a multiple of 100
michael@0 1004
michael@0 1005 // Check if the rate has changed since the last time we came through
michael@0 1006 // I realize it may be overkill to check if the rate has changed, but
michael@0 1007 // I believe it is possible (e.g. if we change sources) and it costs us
michael@0 1008 // very little to handle this case
michael@0 1009
michael@0 1010 if (samplenum_10ms_ != rate/100) {
michael@0 1011 // Determine number of samples in 10 ms from the rate:
michael@0 1012 samplenum_10ms_ = rate/100;
michael@0 1013 // If we switch sample rates (e.g. if we switch codecs),
michael@0 1014 // we throw away what was in the sample_10ms_buffer at the old rate
michael@0 1015 samples_10ms_buffer_ = new int16_t[samplenum_10ms_];
michael@0 1016 buffer_current_ = 0;
michael@0 1017 }
michael@0 1018
michael@0 1019 // Vars to handle the non-sunny-day case (where the audio chunks
michael@0 1020 // we got are not multiples of 10ms OR there were samples left over
michael@0 1021 // from the last run)
michael@0 1022 int64_t chunk_remaining;
michael@0 1023 int64_t tocpy;
michael@0 1024 int16_t *samples_tmp = samples.get();
michael@0 1025
michael@0 1026 chunk_remaining = chunk.mDuration;
michael@0 1027
michael@0 1028 MOZ_ASSERT(chunk_remaining >= 0);
michael@0 1029
michael@0 1030 if (buffer_current_) {
michael@0 1031 tocpy = std::min(chunk_remaining, samplenum_10ms_ - buffer_current_);
michael@0 1032 memcpy(&samples_10ms_buffer_[buffer_current_], samples_tmp, tocpy * sizeof(int16_t));
michael@0 1033 buffer_current_ += tocpy;
michael@0 1034 samples_tmp += tocpy;
michael@0 1035 chunk_remaining -= tocpy;
michael@0 1036
michael@0 1037 if (buffer_current_ == samplenum_10ms_) {
michael@0 1038 // Send out the audio buffer we just finished filling
michael@0 1039 conduit->SendAudioFrame(samples_10ms_buffer_, samplenum_10ms_, rate, 0);
michael@0 1040 buffer_current_ = 0;
michael@0 1041 } else {
michael@0 1042 // We still don't have enough data to send a buffer
michael@0 1043 return;
michael@0 1044 }
michael@0 1045 }
michael@0 1046
michael@0 1047 // Now send (more) frames if there is more than 10ms of input left
michael@0 1048 tocpy = (chunk_remaining / samplenum_10ms_) * samplenum_10ms_;
michael@0 1049 if (tocpy > 0) {
michael@0 1050 conduit->SendAudioFrame(samples_tmp, tocpy, rate, 0);
michael@0 1051 samples_tmp += tocpy;
michael@0 1052 chunk_remaining -= tocpy;
michael@0 1053 }
michael@0 1054 // Copy what remains for the next run
michael@0 1055
michael@0 1056 MOZ_ASSERT(chunk_remaining < samplenum_10ms_);
michael@0 1057
michael@0 1058 if (chunk_remaining) {
michael@0 1059 memcpy(samples_10ms_buffer_, samples_tmp, chunk_remaining * sizeof(int16_t));
michael@0 1060 buffer_current_ = chunk_remaining;
michael@0 1061 }
michael@0 1062
michael@0 1063 }
michael@0 1064
michael@0 1065 #ifdef MOZILLA_INTERNAL_API
michael@0 1066 void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk(
michael@0 1067 VideoSessionConduit* conduit,
michael@0 1068 TrackRate rate,
michael@0 1069 VideoChunk& chunk) {
michael@0 1070 layers::Image *img = chunk.mFrame.GetImage();
michael@0 1071
michael@0 1072 // We now need to send the video frame to the other side
michael@0 1073 if (!img) {
michael@0 1074 // segment.AppendFrame() allows null images, which show up here as null
michael@0 1075 return;
michael@0 1076 }
michael@0 1077
michael@0 1078 gfx::IntSize size = img->GetSize();
michael@0 1079 if ((size.width & 1) != 0 || (size.height & 1) != 0) {
michael@0 1080 MOZ_ASSERT(false, "Can't handle odd-sized images");
michael@0 1081 return;
michael@0 1082 }
michael@0 1083
michael@0 1084 if (chunk.mFrame.GetForceBlack()) {
michael@0 1085 uint32_t yPlaneLen = size.width*size.height;
michael@0 1086 uint32_t cbcrPlaneLen = yPlaneLen/2;
michael@0 1087 uint32_t length = yPlaneLen + cbcrPlaneLen;
michael@0 1088
michael@0 1089 // Send a black image.
michael@0 1090 nsAutoArrayPtr<uint8_t> pixelData;
michael@0 1091 static const fallible_t fallible = fallible_t();
michael@0 1092 pixelData = new (fallible) uint8_t[length];
michael@0 1093 if (pixelData) {
michael@0 1094 memset(pixelData, 0x10, yPlaneLen);
michael@0 1095 // Fill Cb/Cr planes
michael@0 1096 memset(pixelData + yPlaneLen, 0x80, cbcrPlaneLen);
michael@0 1097
michael@0 1098 MOZ_MTLOG(ML_DEBUG, "Sending a black video frame");
michael@0 1099 conduit->SendVideoFrame(pixelData, length, size.width, size.height,
michael@0 1100 mozilla::kVideoI420, 0);
michael@0 1101 }
michael@0 1102 return;
michael@0 1103 }
michael@0 1104
michael@0 1105 // We get passed duplicate frames every ~10ms even if there's no frame change!
michael@0 1106 int32_t serial = img->GetSerial();
michael@0 1107 if (serial == last_img_) {
michael@0 1108 return;
michael@0 1109 }
michael@0 1110 last_img_ = serial;
michael@0 1111
michael@0 1112 ImageFormat format = img->GetFormat();
michael@0 1113 #ifdef WEBRTC_GONK
michael@0 1114 if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
michael@0 1115 layers::GrallocImage *nativeImage = static_cast<layers::GrallocImage*>(img);
michael@0 1116 android::sp<android::GraphicBuffer> graphicBuffer = nativeImage->GetGraphicBuffer();
michael@0 1117 void *basePtr;
michael@0 1118 graphicBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_MASK, &basePtr);
michael@0 1119 conduit->SendVideoFrame(static_cast<unsigned char*>(basePtr),
michael@0 1120 (graphicBuffer->getWidth() * graphicBuffer->getHeight() * 3) / 2,
michael@0 1121 graphicBuffer->getWidth(),
michael@0 1122 graphicBuffer->getHeight(),
michael@0 1123 mozilla::kVideoNV21, 0);
michael@0 1124 graphicBuffer->unlock();
michael@0 1125 } else
michael@0 1126 #endif
michael@0 1127 if (format == ImageFormat::PLANAR_YCBCR) {
michael@0 1128 // Cast away constness b/c some of the accessors are non-const
michael@0 1129 layers::PlanarYCbCrImage* yuv =
michael@0 1130 const_cast<layers::PlanarYCbCrImage *>(
michael@0 1131 static_cast<const layers::PlanarYCbCrImage *>(img));
michael@0 1132 // Big-time assumption here that this is all contiguous data coming
michael@0 1133 // from getUserMedia or other sources.
michael@0 1134 const layers::PlanarYCbCrData *data = yuv->GetData();
michael@0 1135
michael@0 1136 uint8_t *y = data->mYChannel;
michael@0 1137 #ifdef DEBUG
michael@0 1138 uint8_t *cb = data->mCbChannel;
michael@0 1139 uint8_t *cr = data->mCrChannel;
michael@0 1140 #endif
michael@0 1141 uint32_t width = yuv->GetSize().width;
michael@0 1142 uint32_t height = yuv->GetSize().height;
michael@0 1143 uint32_t length = yuv->GetDataSize();
michael@0 1144
michael@0 1145 // SendVideoFrame only supports contiguous YCrCb 4:2:0 buffers
michael@0 1146 // Verify it's contiguous and in the right order
michael@0 1147 MOZ_ASSERT(cb == (y + width*height) &&
michael@0 1148 cr == (cb + width*height/4));
michael@0 1149 // XXX Consider making this a non-debug-only check if we ever implement
michael@0 1150 // any subclasses of PlanarYCbCrImage that allow disjoint buffers such
michael@0 1151 // that y+3(width*height)/2 might go outside the allocation.
michael@0 1152 // GrallocImage can have wider strides, and so in some cases
michael@0 1153 // would encode as garbage. If we need to encode it we'll either want to
michael@0 1154 // modify SendVideoFrame or copy/move the data in the buffer.
michael@0 1155
michael@0 1156 // OK, pass it on to the conduit
michael@0 1157 MOZ_MTLOG(ML_DEBUG, "Sending a video frame");
michael@0 1158 // Not much for us to do with an error
michael@0 1159 conduit->SendVideoFrame(y, length, width, height, mozilla::kVideoI420, 0);
michael@0 1160 } else if(format == ImageFormat::CAIRO_SURFACE) {
michael@0 1161 layers::CairoImage* rgb =
michael@0 1162 const_cast<layers::CairoImage *>(
michael@0 1163 static_cast<const layers::CairoImage *>(img));
michael@0 1164
michael@0 1165 gfx::IntSize size = rgb->GetSize();
michael@0 1166 int half_width = (size.width + 1) >> 1;
michael@0 1167 int half_height = (size.height + 1) >> 1;
michael@0 1168 int c_size = half_width * half_height;
michael@0 1169 int buffer_size = size.width * size.height + 2 * c_size;
michael@0 1170 uint8* yuv = (uint8*) malloc(buffer_size);
michael@0 1171 if (!yuv)
michael@0 1172 return;
michael@0 1173
michael@0 1174 int cb_offset = size.width * size.height;
michael@0 1175 int cr_offset = cb_offset + c_size;
michael@0 1176 RefPtr<gfx::SourceSurface> tempSurf = rgb->GetAsSourceSurface();
michael@0 1177 RefPtr<gfx::DataSourceSurface> surf = tempSurf->GetDataSurface();
michael@0 1178
michael@0 1179 switch (surf->GetFormat()) {
michael@0 1180 case gfx::SurfaceFormat::B8G8R8A8:
michael@0 1181 case gfx::SurfaceFormat::B8G8R8X8:
michael@0 1182 libyuv::ARGBToI420(static_cast<uint8*>(surf->GetData()), surf->Stride(),
michael@0 1183 yuv, size.width,
michael@0 1184 yuv + cb_offset, half_width,
michael@0 1185 yuv + cr_offset, half_width,
michael@0 1186 size.width, size.height);
michael@0 1187 break;
michael@0 1188 case gfx::SurfaceFormat::R5G6B5:
michael@0 1189 libyuv::RGB565ToI420(static_cast<uint8*>(surf->GetData()), surf->Stride(),
michael@0 1190 yuv, size.width,
michael@0 1191 yuv + cb_offset, half_width,
michael@0 1192 yuv + cr_offset, half_width,
michael@0 1193 size.width, size.height);
michael@0 1194 break;
michael@0 1195 default:
michael@0 1196 MOZ_MTLOG(ML_ERROR, "Unsupported RGB video format");
michael@0 1197 MOZ_ASSERT(PR_FALSE);
michael@0 1198 }
michael@0 1199 conduit->SendVideoFrame(yuv, buffer_size, size.width, size.height, mozilla::kVideoI420, 0);
michael@0 1200 } else {
michael@0 1201 MOZ_MTLOG(ML_ERROR, "Unsupported video format");
michael@0 1202 MOZ_ASSERT(PR_FALSE);
michael@0 1203 return;
michael@0 1204 }
michael@0 1205 }
michael@0 1206 #endif
michael@0 1207
michael@0 1208 nsresult MediaPipelineReceiveAudio::Init() {
michael@0 1209 char track_id_string[11];
michael@0 1210 ASSERT_ON_THREAD(main_thread_);
michael@0 1211 MOZ_MTLOG(ML_DEBUG, __FUNCTION__);
michael@0 1212
michael@0 1213 // We can replace this when we are allowed to do streams or std::to_string
michael@0 1214 PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id_);
michael@0 1215
michael@0 1216 description_ = pc_ + "| Receive audio[";
michael@0 1217 description_ += track_id_string;
michael@0 1218 description_ += "]";
michael@0 1219
michael@0 1220 listener_->AddSelf(new AudioSegment());
michael@0 1221
michael@0 1222 return MediaPipelineReceive::Init();
michael@0 1223 }
michael@0 1224
michael@0 1225
michael@0 1226 // Add a track and listener on the MSG thread using the MSG command queue
michael@0 1227 static void AddTrackAndListener(MediaStream* source,
michael@0 1228 TrackID track_id, TrackRate track_rate,
michael@0 1229 MediaStreamListener* listener, MediaSegment* segment,
michael@0 1230 const RefPtr<TrackAddedCallback>& completed) {
michael@0 1231 // This both adds the listener and the track
michael@0 1232 #ifdef MOZILLA_INTERNAL_API
michael@0 1233 class Message : public ControlMessage {
michael@0 1234 public:
michael@0 1235 Message(MediaStream* stream, TrackID track, TrackRate rate,
michael@0 1236 MediaSegment* segment, MediaStreamListener* listener,
michael@0 1237 const RefPtr<TrackAddedCallback>& completed)
michael@0 1238 : ControlMessage(stream),
michael@0 1239 track_id_(track),
michael@0 1240 track_rate_(rate),
michael@0 1241 segment_(segment),
michael@0 1242 listener_(listener),
michael@0 1243 completed_(completed) {}
michael@0 1244
michael@0 1245 virtual void Run() MOZ_OVERRIDE {
michael@0 1246 StreamTime current_end = mStream->GetBufferEnd();
michael@0 1247 TrackTicks current_ticks = TimeToTicksRoundUp(track_rate_, current_end);
michael@0 1248
michael@0 1249 mStream->AddListenerImpl(listener_.forget());
michael@0 1250
michael@0 1251 // Add a track 'now' to avoid possible underrun, especially if we add
michael@0 1252 // a track "later".
michael@0 1253
michael@0 1254 if (current_end != 0L) {
michael@0 1255 MOZ_MTLOG(ML_DEBUG, "added track @ " << current_end <<
michael@0 1256 " -> " << MediaTimeToSeconds(current_end));
michael@0 1257 }
michael@0 1258
michael@0 1259 // To avoid assertions, we need to insert a dummy segment that covers up
michael@0 1260 // to the "start" time for the track
michael@0 1261 segment_->AppendNullData(current_ticks);
michael@0 1262 mStream->AsSourceStream()->AddTrack(track_id_, track_rate_,
michael@0 1263 current_ticks, segment_);
michael@0 1264 // AdvanceKnownTracksTicksTime(HEAT_DEATH_OF_UNIVERSE) means that in
michael@0 1265 // theory per the API, we can't add more tracks before that
michael@0 1266 // time. However, the impl actually allows it, and it avoids a whole
michael@0 1267 // bunch of locking that would be required (and potential blocking)
michael@0 1268 // if we used smaller values and updated them on each NotifyPull.
michael@0 1269 mStream->AsSourceStream()->AdvanceKnownTracksTime(STREAM_TIME_MAX);
michael@0 1270
michael@0 1271 // We need to know how much has been "inserted" because we're given absolute
michael@0 1272 // times in NotifyPull.
michael@0 1273 completed_->TrackAdded(current_ticks);
michael@0 1274 }
michael@0 1275 private:
michael@0 1276 TrackID track_id_;
michael@0 1277 TrackRate track_rate_;
michael@0 1278 MediaSegment* segment_;
michael@0 1279 nsRefPtr<MediaStreamListener> listener_;
michael@0 1280 const RefPtr<TrackAddedCallback> completed_;
michael@0 1281 };
michael@0 1282
michael@0 1283 MOZ_ASSERT(listener);
michael@0 1284
michael@0 1285 source->GraphImpl()->AppendMessage(new Message(source, track_id, track_rate, segment, listener, completed));
michael@0 1286 #else
michael@0 1287 source->AddListener(listener);
michael@0 1288 source->AsSourceStream()->AddTrack(track_id, track_rate, 0, segment);
michael@0 1289 #endif
michael@0 1290 }
michael@0 1291
michael@0 1292 void GenericReceiveListener::AddSelf(MediaSegment* segment) {
michael@0 1293 RefPtr<TrackAddedCallback> callback = new GenericReceiveCallback(this);
michael@0 1294 AddTrackAndListener(source_, track_id_, track_rate_, this, segment, callback);
michael@0 1295 }
michael@0 1296
michael@0 1297 MediaPipelineReceiveAudio::PipelineListener::PipelineListener(
michael@0 1298 SourceMediaStream * source, TrackID track_id,
michael@0 1299 const RefPtr<MediaSessionConduit>& conduit)
michael@0 1300 : GenericReceiveListener(source, track_id, 16000), // XXX rate assumption
michael@0 1301 conduit_(conduit)
michael@0 1302 {
michael@0 1303 MOZ_ASSERT(track_rate_%100 == 0);
michael@0 1304 }
michael@0 1305
michael@0 1306 void MediaPipelineReceiveAudio::PipelineListener::
michael@0 1307 NotifyPull(MediaStreamGraph* graph, StreamTime desired_time) {
michael@0 1308 MOZ_ASSERT(source_);
michael@0 1309 if (!source_) {
michael@0 1310 MOZ_MTLOG(ML_ERROR, "NotifyPull() called from a non-SourceMediaStream");
michael@0 1311 return;
michael@0 1312 }
michael@0 1313
michael@0 1314 // This comparison is done in total time to avoid accumulated roundoff errors.
michael@0 1315 while (TicksToTimeRoundDown(track_rate_, played_ticks_) < desired_time) {
michael@0 1316 // TODO(ekr@rtfm.com): Is there a way to avoid mallocating here? Or reduce the size?
michael@0 1317 // Max size given mono is 480*2*1 = 960 (48KHz)
michael@0 1318 #define AUDIO_SAMPLE_BUFFER_MAX 1000
michael@0 1319 MOZ_ASSERT((track_rate_/100)*sizeof(uint16_t) <= AUDIO_SAMPLE_BUFFER_MAX);
michael@0 1320
michael@0 1321 nsRefPtr<SharedBuffer> samples = SharedBuffer::Create(AUDIO_SAMPLE_BUFFER_MAX);
michael@0 1322 int16_t *samples_data = static_cast<int16_t *>(samples->Data());
michael@0 1323 int samples_length;
michael@0 1324
michael@0 1325 // This fetches 10ms of data
michael@0 1326 MediaConduitErrorCode err =
michael@0 1327 static_cast<AudioSessionConduit*>(conduit_.get())->GetAudioFrame(
michael@0 1328 samples_data,
michael@0 1329 track_rate_,
michael@0 1330 0, // TODO(ekr@rtfm.com): better estimate of "capture" (really playout) delay
michael@0 1331 samples_length);
michael@0 1332 MOZ_ASSERT(samples_length < AUDIO_SAMPLE_BUFFER_MAX);
michael@0 1333
michael@0 1334 if (err != kMediaConduitNoError) {
michael@0 1335 // Insert silence on conduit/GIPS failure (extremely unlikely)
michael@0 1336 MOZ_MTLOG(ML_ERROR, "Audio conduit failed (" << err
michael@0 1337 << ") to return data @ " << played_ticks_
michael@0 1338 << " (desired " << desired_time << " -> "
michael@0 1339 << MediaTimeToSeconds(desired_time) << ")");
michael@0 1340 MOZ_ASSERT(err == kMediaConduitNoError);
michael@0 1341 samples_length = (track_rate_/100)*sizeof(uint16_t); // if this is not enough we'll loop and provide more
michael@0 1342 memset(samples_data, '\0', samples_length);
michael@0 1343 }
michael@0 1344
michael@0 1345 MOZ_MTLOG(ML_DEBUG, "Audio conduit returned buffer of length "
michael@0 1346 << samples_length);
michael@0 1347
michael@0 1348 AudioSegment segment;
michael@0 1349 nsAutoTArray<const int16_t*,1> channels;
michael@0 1350 channels.AppendElement(samples_data);
michael@0 1351 segment.AppendFrames(samples.forget(), channels, samples_length);
michael@0 1352
michael@0 1353 // Handle track not actually added yet or removed/finished
michael@0 1354 if (source_->AppendToTrack(track_id_, &segment)) {
michael@0 1355 played_ticks_ += track_rate_/100; // 10ms in TrackTicks
michael@0 1356 } else {
michael@0 1357 MOZ_MTLOG(ML_ERROR, "AppendToTrack failed");
michael@0 1358 // we can't un-read the data, but that's ok since we don't want to
michael@0 1359 // buffer - but don't i-loop!
michael@0 1360 return;
michael@0 1361 }
michael@0 1362 }
michael@0 1363 }
michael@0 1364
michael@0 1365 nsresult MediaPipelineReceiveVideo::Init() {
michael@0 1366 char track_id_string[11];
michael@0 1367 ASSERT_ON_THREAD(main_thread_);
michael@0 1368 MOZ_MTLOG(ML_DEBUG, __FUNCTION__);
michael@0 1369
michael@0 1370 // We can replace this when we are allowed to do streams or std::to_string
michael@0 1371 PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id_);
michael@0 1372
michael@0 1373 description_ = pc_ + "| Receive video[";
michael@0 1374 description_ += track_id_string;
michael@0 1375 description_ += "]";
michael@0 1376
michael@0 1377 #ifdef MOZILLA_INTERNAL_API
michael@0 1378 listener_->AddSelf(new VideoSegment());
michael@0 1379 #endif
michael@0 1380
michael@0 1381 // Always happens before we can DetachMediaStream()
michael@0 1382 static_cast<VideoSessionConduit *>(conduit_.get())->
michael@0 1383 AttachRenderer(renderer_);
michael@0 1384
michael@0 1385 return MediaPipelineReceive::Init();
michael@0 1386 }
michael@0 1387
michael@0 1388 MediaPipelineReceiveVideo::PipelineListener::PipelineListener(
michael@0 1389 SourceMediaStream* source, TrackID track_id)
michael@0 1390 : GenericReceiveListener(source, track_id, USECS_PER_S),
michael@0 1391 width_(640),
michael@0 1392 height_(480),
michael@0 1393 #ifdef MOZILLA_INTERNAL_API
michael@0 1394 image_container_(),
michael@0 1395 image_(),
michael@0 1396 #endif
michael@0 1397 monitor_("Video PipelineListener") {
michael@0 1398 #ifdef MOZILLA_INTERNAL_API
michael@0 1399 image_container_ = layers::LayerManager::CreateImageContainer();
michael@0 1400 #endif
michael@0 1401 }
michael@0 1402
michael@0 1403 void MediaPipelineReceiveVideo::PipelineListener::RenderVideoFrame(
michael@0 1404 const unsigned char* buffer,
michael@0 1405 unsigned int buffer_size,
michael@0 1406 uint32_t time_stamp,
michael@0 1407 int64_t render_time,
michael@0 1408 const RefPtr<layers::Image>& video_image) {
michael@0 1409 #ifdef MOZILLA_INTERNAL_API
michael@0 1410 ReentrantMonitorAutoEnter enter(monitor_);
michael@0 1411
michael@0 1412 if (buffer) {
michael@0 1413 // Create a video frame using |buffer|.
michael@0 1414 #ifdef MOZ_WIDGET_GONK
michael@0 1415 ImageFormat format = ImageFormat::GRALLOC_PLANAR_YCBCR;
michael@0 1416 #else
michael@0 1417 ImageFormat format = ImageFormat::PLANAR_YCBCR;
michael@0 1418 #endif
michael@0 1419 nsRefPtr<layers::Image> image = image_container_->CreateImage(format);
michael@0 1420 layers::PlanarYCbCrImage* yuvImage = static_cast<layers::PlanarYCbCrImage*>(image.get());
michael@0 1421 uint8_t* frame = const_cast<uint8_t*>(static_cast<const uint8_t*> (buffer));
michael@0 1422 const uint8_t lumaBpp = 8;
michael@0 1423 const uint8_t chromaBpp = 4;
michael@0 1424
michael@0 1425 layers::PlanarYCbCrData yuvData;
michael@0 1426 yuvData.mYChannel = frame;
michael@0 1427 yuvData.mYSize = IntSize(width_, height_);
michael@0 1428 yuvData.mYStride = width_ * lumaBpp/ 8;
michael@0 1429 yuvData.mCbCrStride = width_ * chromaBpp / 8;
michael@0 1430 yuvData.mCbChannel = frame + height_ * yuvData.mYStride;
michael@0 1431 yuvData.mCrChannel = yuvData.mCbChannel + height_ * yuvData.mCbCrStride / 2;
michael@0 1432 yuvData.mCbCrSize = IntSize(width_/ 2, height_/ 2);
michael@0 1433 yuvData.mPicX = 0;
michael@0 1434 yuvData.mPicY = 0;
michael@0 1435 yuvData.mPicSize = IntSize(width_, height_);
michael@0 1436 yuvData.mStereoMode = StereoMode::MONO;
michael@0 1437
michael@0 1438 yuvImage->SetData(yuvData);
michael@0 1439
michael@0 1440 image_ = image.forget();
michael@0 1441 }
michael@0 1442 #ifdef WEBRTC_GONK
michael@0 1443 else {
michael@0 1444 // Decoder produced video frame that can be appended to the track directly.
michael@0 1445 MOZ_ASSERT(video_image);
michael@0 1446 image_ = video_image;
michael@0 1447 }
michael@0 1448 #endif // WEBRTC_GONK
michael@0 1449 #endif // MOZILLA_INTERNAL_API
michael@0 1450 }
michael@0 1451
michael@0 1452 void MediaPipelineReceiveVideo::PipelineListener::
michael@0 1453 NotifyPull(MediaStreamGraph* graph, StreamTime desired_time) {
michael@0 1454 ReentrantMonitorAutoEnter enter(monitor_);
michael@0 1455
michael@0 1456 #ifdef MOZILLA_INTERNAL_API
michael@0 1457 nsRefPtr<layers::Image> image = image_;
michael@0 1458 TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, desired_time);
michael@0 1459 TrackTicks delta = target - played_ticks_;
michael@0 1460
michael@0 1461 // Don't append if we've already provided a frame that supposedly
michael@0 1462 // goes past the current aDesiredTime Doing so means a negative
michael@0 1463 // delta and thus messes up handling of the graph
michael@0 1464 if (delta > 0) {
michael@0 1465 VideoSegment segment;
michael@0 1466 segment.AppendFrame(image.forget(), delta, IntSize(width_, height_));
michael@0 1467 // Handle track not actually added yet or removed/finished
michael@0 1468 if (source_->AppendToTrack(track_id_, &segment)) {
michael@0 1469 played_ticks_ = target;
michael@0 1470 } else {
michael@0 1471 MOZ_MTLOG(ML_ERROR, "AppendToTrack failed");
michael@0 1472 return;
michael@0 1473 }
michael@0 1474 }
michael@0 1475 #endif
michael@0 1476 }
michael@0 1477
michael@0 1478
michael@0 1479 } // end namespace

mercurial