summaryrefslogtreecommitdiff
path: root/media/cast/audio_sender/audio_sender.cc
blob: 00f4313e1e71f3f35ca7c30f5c52c9b06cf197cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/audio_sender/audio_sender.h"

#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
#include "media/cast/audio_sender/audio_encoder.h"
#include "media/cast/cast_environment.h"
#include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtp_sender/rtp_sender.h"

namespace media {
namespace cast {

const int64 kMinSchedulingDelayMs = 1;

class LocalRtcpAudioSenderFeedback : public RtcpSenderFeedback {
 public:
  explicit LocalRtcpAudioSenderFeedback(AudioSender* audio_sender)
      : audio_sender_(audio_sender) {
  }

  virtual void OnReceivedCastFeedback(
      const RtcpCastMessage& cast_feedback) OVERRIDE {
    if (!cast_feedback.missing_frames_and_packets_.empty()) {
      audio_sender_->ResendPackets(cast_feedback.missing_frames_and_packets_);
    }
    VLOG(1) << "Received audio ACK "
            << static_cast<int>(cast_feedback.ack_frame_id_);
  }

 private:
  AudioSender* audio_sender_;
};

class LocalRtpSenderStatistics : public RtpSenderStatistics {
 public:
  explicit LocalRtpSenderStatistics(RtpSender* rtp_sender)
     : rtp_sender_(rtp_sender) {
  }

  virtual void GetStatistics(const base::TimeTicks& now,
                             RtcpSenderInfo* sender_info) OVERRIDE {
    rtp_sender_->RtpStatistics(now, sender_info);
  }

 private:
  RtpSender* rtp_sender_;
};

AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
                         const AudioSenderConfig& audio_config,
                         PacedPacketSender* const paced_packet_sender)
      : cast_environment_(cast_environment),
        rtp_sender_(cast_environment, &audio_config, NULL,
                    paced_packet_sender),
        rtcp_feedback_(new LocalRtcpAudioSenderFeedback(this)),
        rtp_audio_sender_statistics_(
            new LocalRtpSenderStatistics(&rtp_sender_)),
        rtcp_(cast_environment,
              rtcp_feedback_.get(),
              paced_packet_sender,
              rtp_audio_sender_statistics_.get(),
              NULL,
              audio_config.rtcp_mode,
              base::TimeDelta::FromMilliseconds(audio_config.rtcp_interval),
              audio_config.sender_ssrc,
              audio_config.incoming_feedback_ssrc,
              audio_config.rtcp_c_name),
        initialized_(false),
        weak_factory_(this) {
  if (audio_config.aes_iv_mask.size() == kAesKeySize &&
      audio_config.aes_key.size() == kAesKeySize) {
    iv_mask_ = audio_config.aes_iv_mask;
    crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
        crypto::SymmetricKey::AES, audio_config.aes_key);
    encryptor_.reset(new crypto::Encryptor());
    encryptor_->Init(key, crypto::Encryptor::CTR, std::string());
  } else if (audio_config.aes_iv_mask.size() != 0 ||
             audio_config.aes_key.size() != 0) {
    DCHECK(false) << "Invalid crypto configuration";
  }
  if (!audio_config.use_external_encoder) {
    audio_encoder_ = new AudioEncoder(
        cast_environment, audio_config,
        base::Bind(&AudioSender::SendEncodedAudioFrame,
                   weak_factory_.GetWeakPtr()));
  }
}

AudioSender::~AudioSender() {}

void AudioSender::InitializeTimers() {
  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
  if (!initialized_) {
    initialized_ = true;
    ScheduleNextRtcpReport();
  }
}

void AudioSender::InsertAudio(const AudioBus* audio_bus,
                              const base::TimeTicks& recorded_time,
                              const base::Closure& done_callback) {
  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
  DCHECK(audio_encoder_.get()) << "Invalid internal state";
  // TODO(mikhal): Resolve calculation of the audio rtp_timestamp for logging.
  // This is a tmp solution to allow the code to build.
  cast_environment_->Logging()->InsertFrameEvent(kAudioFrameReceived,
        GetVideoRtpTimestamp(recorded_time), kFrameIdUnknown);
  audio_encoder_->InsertAudio(audio_bus, recorded_time, done_callback);
}

void AudioSender::InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame,
                                        const base::TimeTicks& recorded_time,
                                        const base::Closure callback) {
  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
  DCHECK(audio_encoder_.get() == NULL) << "Invalid internal state";

  cast_environment_->Logging()->InsertFrameEvent(kAudioFrameReceived,
      GetVideoRtpTimestamp(recorded_time), kFrameIdUnknown);

  if (encryptor_) {
    EncodedAudioFrame encrypted_frame;
    if (!EncryptAudioFrame(*audio_frame, &encrypted_frame)) {
      // Logging already done.
      return;
    }
    rtp_sender_.IncomingEncodedAudioFrame(&encrypted_frame, recorded_time);
  } else {
    rtp_sender_.IncomingEncodedAudioFrame(audio_frame, recorded_time);
  }
  callback.Run();
}

void AudioSender::SendEncodedAudioFrame(
    scoped_ptr<EncodedAudioFrame> audio_frame,
    const base::TimeTicks& recorded_time) {
  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
  InitializeTimers();
  if (encryptor_) {
    EncodedAudioFrame encrypted_frame;
    if (!EncryptAudioFrame(*audio_frame.get(), &encrypted_frame)) {
      // Logging already done.
      return;
    }
    rtp_sender_.IncomingEncodedAudioFrame(&encrypted_frame, recorded_time);
  } else {
    rtp_sender_.IncomingEncodedAudioFrame(audio_frame.get(), recorded_time);
  }
}

bool AudioSender::EncryptAudioFrame(const EncodedAudioFrame& audio_frame,
                                    EncodedAudioFrame* encrypted_frame) {
  DCHECK(encryptor_) << "Invalid state";

  if (!encryptor_->SetCounter(GetAesNonce(audio_frame.frame_id, iv_mask_))) {
    NOTREACHED() << "Failed to set counter";
    return false;
  }
  if (!encryptor_->Encrypt(audio_frame.data, &encrypted_frame->data)) {
    NOTREACHED() << "Encrypt error";
    return false;
  }
  encrypted_frame->codec = audio_frame.codec;
  encrypted_frame->frame_id = audio_frame.frame_id;
  encrypted_frame->samples = audio_frame.samples;
  return true;
}

void AudioSender::ResendPackets(
    const MissingFramesAndPacketsMap& missing_frames_and_packets) {
  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
  rtp_sender_.ResendPackets(missing_frames_and_packets);
}

void AudioSender::IncomingRtcpPacket(const uint8* packet, size_t length,
                                     const base::Closure callback) {
  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
  rtcp_.IncomingRtcpPacket(packet, length);
  cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
}

void AudioSender::ScheduleNextRtcpReport() {
  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
  base::TimeDelta time_to_next =
      rtcp_.TimeToSendNextRtcpReport() - cast_environment_->Clock()->NowTicks();

  time_to_next = std::max(time_to_next,
      base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));

  cast_environment_->PostDelayedTask(CastEnvironment::MAIN, FROM_HERE,
      base::Bind(&AudioSender::SendRtcpReport, weak_factory_.GetWeakPtr()),
                 time_to_next);
}

void AudioSender::SendRtcpReport() {
  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
  rtcp_.SendRtcpFromRtpSender(NULL);  // TODO(pwestin): add logging.
  ScheduleNextRtcpReport();
}

}  // namespace cast
}  // namespace media