1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <algorithm> 6 #include <deque> 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/files/file_path.h" 11 #include "base/path_service.h" 12 #include "base/threading/thread.h" 13 #include "media/base/decrypt_config.h" 14 #include "media/base/media_log.h" 15 #include "media/base/mock_demuxer_host.h" 16 #include "media/base/test_helpers.h" 17 #include "media/ffmpeg/ffmpeg_common.h" 18 #include "media/filters/ffmpeg_demuxer.h" 19 #include "media/filters/file_data_source.h" 20 #include "media/webm/webm_crypto_helpers.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 23 using ::testing::AnyNumber; 24 using ::testing::DoAll; 25 using ::testing::Exactly; 26 using ::testing::InSequence; 27 using ::testing::Invoke; 28 using ::testing::NotNull; 29 using ::testing::Return; 30 using ::testing::SaveArg; 31 using ::testing::SetArgPointee; 32 using ::testing::StrictMock; 33 using ::testing::WithArgs; 34 using ::testing::_; 35 36 namespace media { 37 38 MATCHER(IsEndOfStreamBuffer, 39 std::string(negation ? "isn't" : "is") + " end of stream") { 40 return arg->end_of_stream(); 41 } 42 43 static void EosOnReadDone(bool* got_eos_buffer, 44 DemuxerStream::Status status, 45 const scoped_refptr<DecoderBuffer>& buffer) { 46 base::MessageLoop::current()->PostTask( 47 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); 48 49 EXPECT_EQ(status, DemuxerStream::kOk); 50 if (buffer->end_of_stream()) { 51 *got_eos_buffer = true; 52 return; 53 } 54 55 EXPECT_TRUE(buffer->data()); 56 EXPECT_GT(buffer->data_size(), 0); 57 *got_eos_buffer = false; 58 }; 59 60 61 // Fixture class to facilitate writing tests. Takes care of setting up the 62 // FFmpeg, pipeline and filter host mocks. 63 class FFmpegDemuxerTest : public testing::Test { 64 protected: 65 FFmpegDemuxerTest() {} 66 67 virtual ~FFmpegDemuxerTest() { 68 if (demuxer_) { 69 WaitableMessageLoopEvent event; 70 demuxer_->Stop(event.GetClosure()); 71 event.RunAndWait(); 72 } 73 } 74 75 void CreateDemuxer(const std::string& name) { 76 CHECK(!demuxer_); 77 78 EXPECT_CALL(host_, SetTotalBytes(_)).Times(AnyNumber()); 79 EXPECT_CALL(host_, AddBufferedByteRange(_, _)).Times(AnyNumber()); 80 EXPECT_CALL(host_, AddBufferedTimeRange(_, _)).Times(AnyNumber()); 81 82 CreateDataSource(name); 83 84 media::FFmpegNeedKeyCB need_key_cb = 85 base::Bind(&FFmpegDemuxerTest::NeedKeyCB, base::Unretained(this)); 86 demuxer_.reset(new FFmpegDemuxer(message_loop_.message_loop_proxy(), 87 data_source_.get(), 88 need_key_cb, 89 new MediaLog())); 90 } 91 92 MOCK_METHOD1(CheckPoint, void(int v)); 93 94 void InitializeDemuxer() { 95 EXPECT_CALL(host_, SetDuration(_)); 96 WaitableMessageLoopEvent event; 97 demuxer_->Initialize(&host_, event.GetPipelineStatusCB()); 98 event.RunAndWaitForStatus(PIPELINE_OK); 99 } 100 101 MOCK_METHOD2(OnReadDoneCalled, void(int, int64)); 102 103 // Verifies that |buffer| has a specific |size| and |timestamp|. 104 // |location| simply indicates where the call to this function was made. 105 // This makes it easier to track down where test failures occur. 106 void OnReadDone(const tracked_objects::Location& location, 107 int size, int64 timestampInMicroseconds, 108 DemuxerStream::Status status, 109 const scoped_refptr<DecoderBuffer>& buffer) { 110 std::string location_str; 111 location.Write(true, false, &location_str); 112 location_str += "\n"; 113 SCOPED_TRACE(location_str); 114 EXPECT_EQ(status, DemuxerStream::kOk); 115 OnReadDoneCalled(size, timestampInMicroseconds); 116 EXPECT_TRUE(buffer.get() != NULL); 117 EXPECT_EQ(size, buffer->data_size()); 118 EXPECT_EQ(base::TimeDelta::FromMicroseconds(timestampInMicroseconds), 119 buffer->timestamp()); 120 121 DCHECK_EQ(&message_loop_, base::MessageLoop::current()); 122 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); 123 } 124 125 DemuxerStream::ReadCB NewReadCB(const tracked_objects::Location& location, 126 int size, int64 timestampInMicroseconds) { 127 EXPECT_CALL(*this, OnReadDoneCalled(size, timestampInMicroseconds)); 128 return base::Bind(&FFmpegDemuxerTest::OnReadDone, base::Unretained(this), 129 location, size, timestampInMicroseconds); 130 } 131 132 // TODO(xhwang): This is a workaround of the issue that move-only parameters 133 // are not supported in mocked methods. Remove this when the issue is fixed 134 // (http://code.google.com/p/googletest/issues/detail?id=395) or when we use 135 // std::string instead of scoped_ptr<uint8[]> (http://crbug.com/130689). 136 MOCK_METHOD3(NeedKeyCBMock, void(const std::string& type, 137 const uint8* init_data, int init_data_size)); 138 void NeedKeyCB(const std::string& type, 139 scoped_ptr<uint8[]> init_data, int init_data_size) { 140 NeedKeyCBMock(type, init_data.get(), init_data_size); 141 } 142 143 // Accessor to demuxer internals. 144 void set_duration_known(bool duration_known) { 145 demuxer_->duration_known_ = duration_known; 146 } 147 148 bool IsStreamStopped(DemuxerStream::Type type) { 149 DemuxerStream* stream = demuxer_->GetStream(type); 150 CHECK(stream); 151 return !static_cast<FFmpegDemuxerStream*>(stream)->demuxer_; 152 } 153 154 // Fixture members. 155 scoped_ptr<FileDataSource> data_source_; 156 scoped_ptr<FFmpegDemuxer> demuxer_; 157 StrictMock<MockDemuxerHost> host_; 158 base::MessageLoop message_loop_; 159 160 AVFormatContext* format_context() { 161 return demuxer_->glue_->format_context(); 162 } 163 164 void ReadUntilEndOfStream() { 165 // We should expect an end of stream buffer. 166 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); 167 168 bool got_eos_buffer = false; 169 const int kMaxBuffers = 170; 170 for (int i = 0; !got_eos_buffer && i < kMaxBuffers; i++) { 171 audio->Read(base::Bind(&EosOnReadDone, &got_eos_buffer)); 172 message_loop_.Run(); 173 } 174 175 EXPECT_TRUE(got_eos_buffer); 176 } 177 178 private: 179 void CreateDataSource(const std::string& name) { 180 CHECK(!data_source_); 181 182 base::FilePath file_path; 183 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); 184 185 file_path = file_path.Append(FILE_PATH_LITERAL("media")) 186 .Append(FILE_PATH_LITERAL("test")) 187 .Append(FILE_PATH_LITERAL("data")) 188 .AppendASCII(name); 189 190 data_source_.reset(new FileDataSource()); 191 EXPECT_TRUE(data_source_->Initialize(file_path)); 192 } 193 194 DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerTest); 195 }; 196 197 TEST_F(FFmpegDemuxerTest, Initialize_OpenFails) { 198 // Simulate avformat_open_input() failing. 199 CreateDemuxer("ten_byte_file"); 200 WaitableMessageLoopEvent event; 201 demuxer_->Initialize(&host_, event.GetPipelineStatusCB()); 202 event.RunAndWaitForStatus(DEMUXER_ERROR_COULD_NOT_OPEN); 203 } 204 205 // TODO(acolwell): Uncomment this test when we discover a file that passes 206 // avformat_open_input(), but has avformat_find_stream_info() fail. 207 // 208 //TEST_F(FFmpegDemuxerTest, Initialize_ParseFails) { 209 // ("find_stream_info_fail.webm"); 210 // demuxer_->Initialize( 211 // &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_PARSE)); 212 // message_loop_.RunUntilIdle(); 213 //} 214 215 TEST_F(FFmpegDemuxerTest, Initialize_NoStreams) { 216 // Open a file with no streams whatsoever. 217 CreateDemuxer("no_streams.webm"); 218 WaitableMessageLoopEvent event; 219 demuxer_->Initialize(&host_, event.GetPipelineStatusCB()); 220 event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); 221 } 222 223 TEST_F(FFmpegDemuxerTest, Initialize_NoAudioVideo) { 224 // Open a file containing streams but none of which are audio/video streams. 225 CreateDemuxer("no_audio_video.webm"); 226 WaitableMessageLoopEvent event; 227 demuxer_->Initialize(&host_, event.GetPipelineStatusCB()); 228 event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); 229 } 230 231 TEST_F(FFmpegDemuxerTest, Initialize_Successful) { 232 CreateDemuxer("bear-320x240.webm"); 233 InitializeDemuxer(); 234 235 // Video stream should be present. 236 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); 237 ASSERT_TRUE(stream); 238 EXPECT_EQ(DemuxerStream::VIDEO, stream->type()); 239 240 const VideoDecoderConfig& video_config = stream->video_decoder_config(); 241 EXPECT_EQ(kCodecVP8, video_config.codec()); 242 EXPECT_EQ(VideoFrame::YV12, video_config.format()); 243 EXPECT_EQ(320, video_config.coded_size().width()); 244 EXPECT_EQ(240, video_config.coded_size().height()); 245 EXPECT_EQ(0, video_config.visible_rect().x()); 246 EXPECT_EQ(0, video_config.visible_rect().y()); 247 EXPECT_EQ(320, video_config.visible_rect().width()); 248 EXPECT_EQ(240, video_config.visible_rect().height()); 249 EXPECT_EQ(320, video_config.natural_size().width()); 250 EXPECT_EQ(240, video_config.natural_size().height()); 251 EXPECT_FALSE(video_config.extra_data()); 252 EXPECT_EQ(0u, video_config.extra_data_size()); 253 254 // Audio stream should be present. 255 stream = demuxer_->GetStream(DemuxerStream::AUDIO); 256 ASSERT_TRUE(stream); 257 EXPECT_EQ(DemuxerStream::AUDIO, stream->type()); 258 259 const AudioDecoderConfig& audio_config = stream->audio_decoder_config(); 260 EXPECT_EQ(kCodecVorbis, audio_config.codec()); 261 EXPECT_EQ(32, audio_config.bits_per_channel()); 262 EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout()); 263 EXPECT_EQ(44100, audio_config.samples_per_second()); 264 EXPECT_EQ(kSampleFormatPlanarF32, audio_config.sample_format()); 265 EXPECT_TRUE(audio_config.extra_data()); 266 EXPECT_GT(audio_config.extra_data_size(), 0u); 267 268 // Unknown stream should never be present. 269 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN)); 270 } 271 272 TEST_F(FFmpegDemuxerTest, Initialize_Multitrack) { 273 // Open a file containing the following streams: 274 // Stream #0: Video (VP8) 275 // Stream #1: Audio (Vorbis) 276 // Stream #2: Subtitles (SRT) 277 // Stream #3: Video (Theora) 278 // Stream #4: Audio (16-bit signed little endian PCM) 279 // 280 // We should only pick the first audio/video streams we come across. 281 CreateDemuxer("bear-320x240-multitrack.webm"); 282 InitializeDemuxer(); 283 284 // Video stream should be VP8. 285 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); 286 ASSERT_TRUE(stream); 287 EXPECT_EQ(DemuxerStream::VIDEO, stream->type()); 288 EXPECT_EQ(kCodecVP8, stream->video_decoder_config().codec()); 289 290 // Audio stream should be Vorbis. 291 stream = demuxer_->GetStream(DemuxerStream::AUDIO); 292 ASSERT_TRUE(stream); 293 EXPECT_EQ(DemuxerStream::AUDIO, stream->type()); 294 EXPECT_EQ(kCodecVorbis, stream->audio_decoder_config().codec()); 295 296 // Unknown stream should never be present. 297 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN)); 298 } 299 300 TEST_F(FFmpegDemuxerTest, Initialize_Encrypted) { 301 EXPECT_CALL(*this, NeedKeyCBMock(kWebMEncryptInitDataType, NotNull(), 302 DecryptConfig::kDecryptionKeySize)) 303 .Times(Exactly(2)); 304 305 CreateDemuxer("bear-320x240-av_enc-av.webm"); 306 InitializeDemuxer(); 307 } 308 309 TEST_F(FFmpegDemuxerTest, Read_Audio) { 310 // We test that on a successful audio packet read. 311 CreateDemuxer("bear-320x240.webm"); 312 InitializeDemuxer(); 313 314 // Attempt a read from the audio stream and run the message loop until done. 315 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); 316 317 audio->Read(NewReadCB(FROM_HERE, 29, 0)); 318 message_loop_.Run(); 319 320 audio->Read(NewReadCB(FROM_HERE, 27, 3000)); 321 message_loop_.Run(); 322 } 323 324 TEST_F(FFmpegDemuxerTest, Read_Video) { 325 // We test that on a successful video packet read. 326 CreateDemuxer("bear-320x240.webm"); 327 InitializeDemuxer(); 328 329 // Attempt a read from the video stream and run the message loop until done. 330 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO); 331 332 video->Read(NewReadCB(FROM_HERE, 22084, 0)); 333 message_loop_.Run(); 334 335 video->Read(NewReadCB(FROM_HERE, 1057, 33000)); 336 message_loop_.Run(); 337 } 338 339 TEST_F(FFmpegDemuxerTest, Read_VideoNonZeroStart) { 340 // Test the start time is the first timestamp of the video and audio stream. 341 CreateDemuxer("nonzero-start-time.webm"); 342 InitializeDemuxer(); 343 344 // Attempt a read from the video stream and run the message loop until done. 345 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO); 346 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); 347 348 // Check first buffer in video stream. 349 video->Read(NewReadCB(FROM_HERE, 5636, 400000)); 350 message_loop_.Run(); 351 352 // Check first buffer in audio stream. 353 audio->Read(NewReadCB(FROM_HERE, 165, 396000)); 354 message_loop_.Run(); 355 356 // Verify that the start time is equal to the lowest timestamp (ie the audio). 357 EXPECT_EQ(demuxer_->GetStartTime().InMicroseconds(), 396000); 358 } 359 360 TEST_F(FFmpegDemuxerTest, Read_EndOfStream) { 361 // Verify that end of stream buffers are created. 362 CreateDemuxer("bear-320x240.webm"); 363 InitializeDemuxer(); 364 ReadUntilEndOfStream(); 365 } 366 367 TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration) { 368 // Verify that end of stream buffers are created. 369 CreateDemuxer("bear-320x240.webm"); 370 InitializeDemuxer(); 371 set_duration_known(false); 372 EXPECT_CALL(host_, SetDuration(_)); 373 ReadUntilEndOfStream(); 374 } 375 376 TEST_F(FFmpegDemuxerTest, Seek) { 377 // We're testing that the demuxer frees all queued packets when it receives 378 // a Seek(). 379 CreateDemuxer("bear-320x240.webm"); 380 InitializeDemuxer(); 381 382 // Get our streams. 383 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO); 384 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); 385 ASSERT_TRUE(video); 386 ASSERT_TRUE(audio); 387 388 // Read a video packet and release it. 389 video->Read(NewReadCB(FROM_HERE, 22084, 0)); 390 message_loop_.Run(); 391 392 // Issue a simple forward seek, which should discard queued packets. 393 WaitableMessageLoopEvent event; 394 demuxer_->Seek(base::TimeDelta::FromMicroseconds(1000000), 395 event.GetPipelineStatusCB()); 396 event.RunAndWaitForStatus(PIPELINE_OK); 397 398 // Audio read #1. 399 audio->Read(NewReadCB(FROM_HERE, 145, 803000)); 400 message_loop_.Run(); 401 402 // Audio read #2. 403 audio->Read(NewReadCB(FROM_HERE, 148, 826000)); 404 message_loop_.Run(); 405 406 // Video read #1. 407 video->Read(NewReadCB(FROM_HERE, 5425, 801000)); 408 message_loop_.Run(); 409 410 // Video read #2. 411 video->Read(NewReadCB(FROM_HERE, 1906, 834000)); 412 message_loop_.Run(); 413 } 414 415 class MockReadCB { 416 public: 417 MockReadCB() {} 418 ~MockReadCB() {} 419 420 MOCK_METHOD2(Run, void(DemuxerStream::Status status, 421 const scoped_refptr<DecoderBuffer>& buffer)); 422 private: 423 DISALLOW_COPY_AND_ASSIGN(MockReadCB); 424 }; 425 426 TEST_F(FFmpegDemuxerTest, Stop) { 427 // Tests that calling Read() on a stopped demuxer stream immediately deletes 428 // the callback. 429 CreateDemuxer("bear-320x240.webm"); 430 InitializeDemuxer(); 431 432 // Get our stream. 433 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); 434 ASSERT_TRUE(audio); 435 436 demuxer_->Stop(NewExpectedClosure()); 437 438 // Reads after being stopped are all EOS buffers. 439 StrictMock<MockReadCB> callback; 440 EXPECT_CALL(callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer())); 441 442 // Attempt the read... 443 audio->Read(base::Bind(&MockReadCB::Run, base::Unretained(&callback))); 444 message_loop_.RunUntilIdle(); 445 } 446 447 TEST_F(FFmpegDemuxerTest, DisableAudioStream) { 448 // We are doing the following things here: 449 // 1. Initialize the demuxer with audio and video stream. 450 // 2. Send a "disable audio stream" message to the demuxer. 451 // 3. Demuxer will free audio packets even if audio stream was initialized. 452 CreateDemuxer("bear-320x240.webm"); 453 InitializeDemuxer(); 454 455 // Submit a "disable audio stream" message to the demuxer. 456 demuxer_->OnAudioRendererDisabled(); 457 message_loop_.RunUntilIdle(); 458 459 // Get our streams. 460 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO); 461 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); 462 ASSERT_TRUE(video); 463 ASSERT_TRUE(audio); 464 465 // The audio stream should have been prematurely stopped. 466 EXPECT_FALSE(IsStreamStopped(DemuxerStream::VIDEO)); 467 EXPECT_TRUE(IsStreamStopped(DemuxerStream::AUDIO)); 468 469 // Attempt a read from the video stream: it should return valid data. 470 video->Read(NewReadCB(FROM_HERE, 22084, 0)); 471 message_loop_.Run(); 472 473 // Attempt a read from the audio stream: it should immediately return end of 474 // stream without requiring the message loop to read data. 475 bool got_eos_buffer = false; 476 audio->Read(base::Bind(&EosOnReadDone, &got_eos_buffer)); 477 message_loop_.RunUntilIdle(); 478 EXPECT_TRUE(got_eos_buffer); 479 } 480 481 // Verify that seek works properly when the WebM cues data is at the start of 482 // the file instead of at the end. 483 TEST_F(FFmpegDemuxerTest, SeekWithCuesBeforeFirstCluster) { 484 CreateDemuxer("bear-320x240-cues-in-front.webm"); 485 InitializeDemuxer(); 486 487 // Get our streams. 488 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO); 489 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); 490 ASSERT_TRUE(video); 491 ASSERT_TRUE(audio); 492 493 // Read a video packet and release it. 494 video->Read(NewReadCB(FROM_HERE, 22084, 0)); 495 message_loop_.Run(); 496 497 // Issue a simple forward seek, which should discard queued packets. 498 WaitableMessageLoopEvent event; 499 demuxer_->Seek(base::TimeDelta::FromMicroseconds(2500000), 500 event.GetPipelineStatusCB()); 501 event.RunAndWaitForStatus(PIPELINE_OK); 502 503 // Audio read #1. 504 audio->Read(NewReadCB(FROM_HERE, 40, 2403000)); 505 message_loop_.Run(); 506 507 // Audio read #2. 508 audio->Read(NewReadCB(FROM_HERE, 42, 2406000)); 509 message_loop_.Run(); 510 511 // Video read #1. 512 video->Read(NewReadCB(FROM_HERE, 5276, 2402000)); 513 message_loop_.Run(); 514 515 // Video read #2. 516 video->Read(NewReadCB(FROM_HERE, 1740, 2436000)); 517 message_loop_.Run(); 518 } 519 520 // Ensure ID3v1 tag reading is disabled. id3_test.mp3 has an ID3v1 tag with the 521 // field "title" set to "sample for id3 test". 522 TEST_F(FFmpegDemuxerTest, NoID3TagData) { 523 #if !defined(USE_PROPRIETARY_CODECS) 524 return; 525 #endif 526 CreateDemuxer("id3_test.mp3"); 527 InitializeDemuxer(); 528 EXPECT_FALSE(av_dict_get(format_context()->metadata, "title", NULL, 0)); 529 } 530 531 // Ensure MP3 files with large image/video based ID3 tags demux okay. FFmpeg 532 // will hand us a video stream to the data which will likely be in a format we 533 // don't accept as video; e.g. PNG. 534 TEST_F(FFmpegDemuxerTest, Mp3WithVideoStreamID3TagData) { 535 #if !defined(USE_PROPRIETARY_CODECS) 536 return; 537 #endif 538 CreateDemuxer("id3_png_test.mp3"); 539 InitializeDemuxer(); 540 541 // Ensure the expected streams are present. 542 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO)); 543 EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO)); 544 } 545 546 // Ensure a video with an unsupported audio track still results in the video 547 // stream being demuxed. 548 TEST_F(FFmpegDemuxerTest, UnsupportedAudioSupportedVideoDemux) { 549 CreateDemuxer("speex_audio_vorbis_video.ogv"); 550 InitializeDemuxer(); 551 552 // Ensure the expected streams are present. 553 EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::VIDEO)); 554 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::AUDIO)); 555 } 556 557 // Ensure a video with an unsupported video track still results in the audio 558 // stream being demuxed. 559 TEST_F(FFmpegDemuxerTest, UnsupportedVideoSupportedAudioDemux) { 560 CreateDemuxer("vorbis_audio_wmv_video.mkv"); 561 InitializeDemuxer(); 562 563 // Ensure the expected streams are present. 564 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO)); 565 EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO)); 566 } 567 568 // FFmpeg returns null data pointers when samples have zero size, leading to 569 // mistakenly creating end of stream buffers http://crbug.com/169133 570 TEST_F(FFmpegDemuxerTest, MP4_ZeroStszEntry) { 571 #if !defined(USE_PROPRIETARY_CODECS) 572 return; 573 #endif 574 CreateDemuxer("bear-1280x720-zero-stsz-entry.mp4"); 575 InitializeDemuxer(); 576 ReadUntilEndOfStream(); 577 } 578 579 } // namespace media 580