| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/muxers/webm_muxer.h" | 5 #include "media/muxers/webm_muxer.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 using ::testing::InSequence; | 26 using ::testing::InSequence; |
| 27 using ::testing::Mock; | 27 using ::testing::Mock; |
| 28 using ::testing::Not; | 28 using ::testing::Not; |
| 29 using ::testing::Sequence; | 29 using ::testing::Sequence; |
| 30 using ::testing::TestWithParam; | 30 using ::testing::TestWithParam; |
| 31 using ::testing::ValuesIn; | 31 using ::testing::ValuesIn; |
| 32 using ::testing::WithArgs; | 32 using ::testing::WithArgs; |
| 33 | 33 |
| 34 namespace media { | 34 namespace media { |
| 35 | 35 |
| 36 struct kTestParams { | 36 struct TestParams { |
| 37 VideoCodec codec; | 37 VideoCodec codec; |
| 38 size_t num_video_tracks; | 38 size_t num_video_tracks; |
| 39 size_t num_audio_tracks; | 39 size_t num_audio_tracks; |
| 40 }; | 40 }; |
| 41 | 41 |
| 42 class WebmMuxerTest : public TestWithParam<kTestParams> { | 42 class WebmMuxerTest : public TestWithParam<TestParams> { |
| 43 public: | 43 public: |
| 44 WebmMuxerTest() | 44 WebmMuxerTest() |
| 45 : webm_muxer_( | 45 : webm_muxer_( |
| 46 GetParam().codec, | 46 GetParam().codec, |
| 47 GetParam().num_video_tracks, | 47 GetParam().num_video_tracks, |
| 48 GetParam().num_audio_tracks, | 48 GetParam().num_audio_tracks, |
| 49 base::Bind(&WebmMuxerTest::WriteCallback, base::Unretained(this))), | 49 base::Bind(&WebmMuxerTest::WriteCallback, base::Unretained(this))), |
| 50 last_encoded_length_(0), | 50 last_encoded_length_(0), |
| 51 accumulated_position_(0) { | 51 accumulated_position_(0) { |
| 52 EXPECT_EQ(webm_muxer_.Position(), 0); | 52 EXPECT_EQ(webm_muxer_.Position(), 0); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 | 102 |
| 103 const gfx::Size frame_size(160, 80); | 103 const gfx::Size frame_size(160, 80); |
| 104 const scoped_refptr<VideoFrame> video_frame = | 104 const scoped_refptr<VideoFrame> video_frame = |
| 105 VideoFrame::CreateBlackFrame(frame_size); | 105 VideoFrame::CreateBlackFrame(frame_size); |
| 106 const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); | 106 const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); |
| 107 | 107 |
| 108 EXPECT_CALL(*this, WriteCallback(_)) | 108 EXPECT_CALL(*this, WriteCallback(_)) |
| 109 .Times(AtLeast(1)) | 109 .Times(AtLeast(1)) |
| 110 .WillRepeatedly( | 110 .WillRepeatedly( |
| 111 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); | 111 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); |
| 112 webm_muxer_.OnEncodedVideo(WebmMuxer::VideoParameters(video_frame), | 112 EXPECT_TRUE(webm_muxer_.OnEncodedVideo( |
| 113 base::WrapUnique(new std::string(encoded_data)), | 113 WebmMuxer::VideoParameters(video_frame), |
| 114 base::TimeTicks::Now(), false /* keyframe */); | 114 base::WrapUnique(new std::string(encoded_data)), base::TimeTicks::Now(), |
| 115 false /* keyframe */)); |
| 115 | 116 |
| 116 // First time around WriteCallback() is pinged a number of times to write the | 117 // First time around WriteCallback() is pinged a number of times to write the |
| 117 // Matroska header, but at the end it dumps |encoded_data|. | 118 // Matroska header, but at the end it dumps |encoded_data|. |
| 118 EXPECT_EQ(last_encoded_length_, encoded_data.size()); | 119 EXPECT_EQ(last_encoded_length_, encoded_data.size()); |
| 119 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); | 120 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); |
| 120 EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); | 121 EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); |
| 121 EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); | 122 EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); |
| 122 | 123 |
| 123 const int64_t begin_of_second_block = accumulated_position_; | 124 const int64_t begin_of_second_block = accumulated_position_; |
| 124 EXPECT_CALL(*this, WriteCallback(_)) | 125 EXPECT_CALL(*this, WriteCallback(_)) |
| 125 .Times(AtLeast(1)) | 126 .Times(AtLeast(1)) |
| 126 .WillRepeatedly( | 127 .WillRepeatedly( |
| 127 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); | 128 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); |
| 128 webm_muxer_.OnEncodedVideo(video_frame, | 129 EXPECT_TRUE(webm_muxer_.OnEncodedVideo( |
| 129 base::WrapUnique(new std::string(encoded_data)), | 130 video_frame, base::WrapUnique(new std::string(encoded_data)), |
| 130 base::TimeTicks::Now(), false /* keyframe */); | 131 base::TimeTicks::Now(), false /* keyframe */)); |
| 131 | 132 |
| 132 // The second time around the callbacks should include a SimpleBlock header, | 133 // The second time around the callbacks should include a SimpleBlock header, |
| 133 // namely the track index, a timestamp and a flags byte, for a total of 6B. | 134 // namely the track index, a timestamp and a flags byte, for a total of 6B. |
| 134 EXPECT_EQ(last_encoded_length_, encoded_data.size()); | 135 EXPECT_EQ(last_encoded_length_, encoded_data.size()); |
| 135 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); | 136 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); |
| 136 const uint32_t kSimpleBlockSize = 6u; | 137 const uint32_t kSimpleBlockSize = 6u; |
| 137 EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + | 138 EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + |
| 138 encoded_data.size()), | 139 encoded_data.size()), |
| 139 accumulated_position_); | 140 accumulated_position_); |
| 141 |
| 142 // Force an error in libwebm and expect OnEncodedVideo to fail. |
| 143 webm_muxer_.ForceOneLibWebmErrorForTesting(); |
| 144 EXPECT_FALSE( |
| 145 webm_muxer_.OnEncodedVideo(WebmMuxer::VideoParameters(video_frame), |
| 146 base::MakeUnique<std::string>(encoded_data), |
| 147 base::TimeTicks::Now(), true /* keyframe */)); |
| 140 } | 148 } |
| 141 | 149 |
| 142 TEST_P(WebmMuxerTest, OnEncodedAudioTwoFrames) { | 150 TEST_P(WebmMuxerTest, OnEncodedAudioTwoFrames) { |
| 143 if (GetParam().num_video_tracks > 0) | 151 if (GetParam().num_video_tracks > 0) |
| 144 return; | 152 return; |
| 145 | 153 |
| 146 const int sample_rate = 48000; | 154 const int sample_rate = 48000; |
| 147 const int bits_per_sample = 16; | 155 const int bits_per_sample = 16; |
| 148 const int frames_per_buffer = 480; | 156 const int frames_per_buffer = 480; |
| 149 media::AudioParameters audio_params( | 157 media::AudioParameters audio_params( |
| 150 media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, | 158 media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, |
| 151 media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, | 159 media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, |
| 152 frames_per_buffer); | 160 frames_per_buffer); |
| 153 | 161 |
| 154 const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); | 162 const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); |
| 155 | 163 |
| 156 EXPECT_CALL(*this, WriteCallback(_)) | 164 EXPECT_CALL(*this, WriteCallback(_)) |
| 157 .Times(AtLeast(1)) | 165 .Times(AtLeast(1)) |
| 158 .WillRepeatedly( | 166 .WillRepeatedly( |
| 159 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); | 167 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); |
| 160 webm_muxer_.OnEncodedAudio(audio_params, | 168 EXPECT_TRUE(webm_muxer_.OnEncodedAudio( |
| 161 base::MakeUnique<std::string>(encoded_data), | 169 audio_params, base::MakeUnique<std::string>(encoded_data), |
| 162 base::TimeTicks::Now()); | 170 base::TimeTicks::Now())); |
| 163 | 171 |
| 164 // First time around WriteCallback() is pinged a number of times to write the | 172 // First time around WriteCallback() is pinged a number of times to write the |
| 165 // Matroska header, but at the end it dumps |encoded_data|. | 173 // Matroska header, but at the end it dumps |encoded_data|. |
| 166 EXPECT_EQ(last_encoded_length_, encoded_data.size()); | 174 EXPECT_EQ(last_encoded_length_, encoded_data.size()); |
| 167 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); | 175 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); |
| 168 EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); | 176 EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); |
| 169 EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); | 177 EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); |
| 170 | 178 |
| 171 const int64_t begin_of_second_block = accumulated_position_; | 179 const int64_t begin_of_second_block = accumulated_position_; |
| 172 EXPECT_CALL(*this, WriteCallback(_)) | 180 EXPECT_CALL(*this, WriteCallback(_)) |
| 173 .Times(AtLeast(1)) | 181 .Times(AtLeast(1)) |
| 174 .WillRepeatedly( | 182 .WillRepeatedly( |
| 175 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); | 183 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); |
| 176 webm_muxer_.OnEncodedAudio(audio_params, | 184 EXPECT_TRUE(webm_muxer_.OnEncodedAudio( |
| 177 base::MakeUnique<std::string>(encoded_data), | 185 audio_params, base::MakeUnique<std::string>(encoded_data), |
| 178 base::TimeTicks::Now()); | 186 base::TimeTicks::Now())); |
| 179 | 187 |
| 180 // The second time around the callbacks should include a SimpleBlock header, | 188 // The second time around the callbacks should include a SimpleBlock header, |
| 181 // namely the track index, a timestamp and a flags byte, for a total of 6B. | 189 // namely the track index, a timestamp and a flags byte, for a total of 6B. |
| 182 EXPECT_EQ(last_encoded_length_, encoded_data.size()); | 190 EXPECT_EQ(last_encoded_length_, encoded_data.size()); |
| 183 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); | 191 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); |
| 184 const uint32_t kSimpleBlockSize = 6u; | 192 const uint32_t kSimpleBlockSize = 6u; |
| 185 EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + | 193 EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + |
| 186 encoded_data.size()), | 194 encoded_data.size()), |
| 187 accumulated_position_); | 195 accumulated_position_); |
| 196 |
| 197 // Force an error in libwebm and expect OnEncodedAudio to fail. |
| 198 webm_muxer_.ForceOneLibWebmErrorForTesting(); |
| 199 EXPECT_FALSE(webm_muxer_.OnEncodedAudio( |
| 200 audio_params, base::MakeUnique<std::string>(encoded_data), |
| 201 base::TimeTicks::Now())); |
| 188 } | 202 } |
| 189 | 203 |
| 190 // This test verifies that when video data comes before audio data, we save the | 204 // This test verifies that when video data comes before audio data, we save the |
| 191 // encoded video frames and add it to the video track when audio data arrives. | 205 // encoded video frames and add it to the video track when audio data arrives. |
| 192 TEST_P(WebmMuxerTest, VideoIsStoredWhileWaitingForAudio) { | 206 TEST_P(WebmMuxerTest, VideoIsStoredWhileWaitingForAudio) { |
| 193 // This test is only relevant if we have both kinds of tracks. | 207 // This test is only relevant if we have both kinds of tracks. |
| 194 if (GetParam().num_video_tracks == 0 || GetParam().num_audio_tracks == 0) | 208 if (GetParam().num_video_tracks == 0 || GetParam().num_audio_tracks == 0) |
| 195 return; | 209 return; |
| 196 | 210 |
| 197 // First send a video keyframe | 211 // First send a video keyframe. |
| 198 const gfx::Size frame_size(160, 80); | 212 const gfx::Size frame_size(160, 80); |
| 199 const scoped_refptr<VideoFrame> video_frame = | 213 const scoped_refptr<VideoFrame> video_frame = |
| 200 VideoFrame::CreateBlackFrame(frame_size); | 214 VideoFrame::CreateBlackFrame(frame_size); |
| 201 const std::string encoded_video("thisisanencodedvideopacket"); | 215 const std::string encoded_video("thisisanencodedvideopacket"); |
| 202 webm_muxer_.OnEncodedVideo(WebmMuxer::VideoParameters(video_frame), | 216 EXPECT_TRUE(webm_muxer_.OnEncodedVideo( |
| 203 base::WrapUnique(new std::string(encoded_video)), | 217 WebmMuxer::VideoParameters(video_frame), |
| 204 base::TimeTicks::Now(), true /* keyframe */); | 218 base::WrapUnique(new std::string(encoded_video)), base::TimeTicks::Now(), |
| 219 true /* keyframe */)); |
| 205 // A few encoded non key frames. | 220 // A few encoded non key frames. |
| 206 const int kNumNonKeyFrames = 2; | 221 const int kNumNonKeyFrames = 2; |
| 207 for (int i = 0; i < kNumNonKeyFrames; ++i) { | 222 for (int i = 0; i < kNumNonKeyFrames; ++i) { |
| 208 webm_muxer_.OnEncodedVideo(WebmMuxer::VideoParameters(video_frame), | 223 EXPECT_TRUE(webm_muxer_.OnEncodedVideo( |
| 209 base::WrapUnique(new std::string(encoded_video)), | 224 WebmMuxer::VideoParameters(video_frame), |
| 210 base::TimeTicks::Now(), false /* keyframe */); | 225 base::WrapUnique(new std::string(encoded_video)), |
| 226 base::TimeTicks::Now(), false /* keyframe */)); |
| 211 } | 227 } |
| 212 | 228 |
| 213 // Send some audio. The header will be written and muxing will proceed | |
| 214 // normally. | |
| 215 const int sample_rate = 48000; | 229 const int sample_rate = 48000; |
| 216 const int bits_per_sample = 16; | 230 const int bits_per_sample = 16; |
| 217 const int frames_per_buffer = 480; | 231 const int frames_per_buffer = 480; |
| 218 media::AudioParameters audio_params( | 232 media::AudioParameters audio_params( |
| 219 media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, | 233 media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, |
| 220 media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, | 234 media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, |
| 221 frames_per_buffer); | 235 frames_per_buffer); |
| 222 const std::string encoded_audio("thisisanencodedaudiopacket"); | 236 const std::string encoded_audio("thisisanencodedaudiopacket"); |
| 223 | 237 |
| 224 // We should first get the encoded video frames, then the encoded audio frame. | 238 // Force one libwebm error and verify OnEncodedAudio() fails. |
| 239 webm_muxer_.ForceOneLibWebmErrorForTesting(); |
| 240 EXPECT_FALSE(webm_muxer_.OnEncodedAudio( |
| 241 audio_params, base::WrapUnique(new std::string(encoded_audio)), |
| 242 base::TimeTicks::Now())); |
| 243 |
| 244 // We should get the queued encoded video frames, then an encoded audio frame. |
| 225 Sequence s; | 245 Sequence s; |
| 226 EXPECT_CALL(*this, WriteCallback(Eq(encoded_video))) | 246 EXPECT_CALL(*this, WriteCallback(Eq(encoded_video))) |
| 227 .Times(1 + kNumNonKeyFrames) | 247 .Times(1 + kNumNonKeyFrames) |
| 228 .InSequence(s); | 248 .InSequence(s); |
| 229 EXPECT_CALL(*this, WriteCallback(Eq(encoded_audio))).Times(1).InSequence(s); | 249 EXPECT_CALL(*this, WriteCallback(Eq(encoded_audio))).Times(1).InSequence(s); |
| 230 // We'll also get lots of other header-related stuff. | 250 // We'll also get lots of other header-related stuff. |
| 231 EXPECT_CALL(*this, WriteCallback( | 251 EXPECT_CALL(*this, WriteCallback( |
| 232 AllOf(Not(Eq(encoded_video)), Not(Eq(encoded_audio))))) | 252 AllOf(Not(Eq(encoded_video)), Not(Eq(encoded_audio))))) |
| 233 .Times(AnyNumber()); | 253 .Times(AnyNumber()); |
| 234 webm_muxer_.OnEncodedAudio(audio_params, | 254 EXPECT_TRUE(webm_muxer_.OnEncodedAudio( |
| 235 base::WrapUnique(new std::string(encoded_audio)), | 255 audio_params, base::WrapUnique(new std::string(encoded_audio)), |
| 236 base::TimeTicks::Now()); | 256 base::TimeTicks::Now())); |
| 237 } | 257 } |
| 238 | 258 |
| 239 const kTestParams kTestCases[] = { | 259 const TestParams kTestCases[] = { |
| 240 {kCodecVP8, 1 /* num_video_tracks */, 0 /*num_audio_tracks*/}, | 260 {kCodecVP8, 1 /* num_video_tracks */, 0 /*num_audio_tracks*/}, |
| 241 {kCodecVP8, 0, 1}, | 261 {kCodecVP8, 0, 1}, |
| 242 {kCodecVP8, 1, 1}, | 262 {kCodecVP8, 1, 1}, |
| 243 {kCodecVP9, 1, 0}, | 263 {kCodecVP9, 1, 0}, |
| 244 {kCodecVP9, 0, 1}, | 264 {kCodecVP9, 0, 1}, |
| 245 {kCodecVP9, 1, 1}, | 265 {kCodecVP9, 1, 1}, |
| 246 {kCodecH264, 1, 0}, | 266 {kCodecH264, 1, 0}, |
| 247 {kCodecH264, 0, 1}, | 267 {kCodecH264, 0, 1}, |
| 248 {kCodecH264, 1, 1}, | 268 {kCodecH264, 1, 1}, |
| 249 }; | 269 }; |
| 250 | 270 |
| 251 INSTANTIATE_TEST_CASE_P(, WebmMuxerTest, ValuesIn(kTestCases)); | 271 INSTANTIATE_TEST_CASE_P(, WebmMuxerTest, ValuesIn(kTestCases)); |
| 252 | 272 |
| 253 } // namespace media | 273 } // namespace media |
| OLD | NEW |