| Index: media/filters/ffmpeg_demuxer_unittest.cc
 | 
| diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
 | 
| index a1614a421297fb83681baa506cbe683d8dd4de22..636b75735469e55e16ce6b63f88be85273c23cc1 100644
 | 
| --- a/media/filters/ffmpeg_demuxer_unittest.cc
 | 
| +++ b/media/filters/ffmpeg_demuxer_unittest.cc
 | 
| @@ -92,13 +92,19 @@ class FFmpegDemuxerTest : public testing::Test {
 | 
|  
 | 
|    MOCK_METHOD1(CheckPoint, void(int v));
 | 
|  
 | 
| -  void InitializeDemuxerText(bool enable_text) {
 | 
| +  void InitializeDemuxerWithTimelineOffset(bool enable_text,
 | 
| +                                           base::Time timeline_offset) {
 | 
|      EXPECT_CALL(host_, SetDuration(_));
 | 
|      WaitableMessageLoopEvent event;
 | 
|      demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), enable_text);
 | 
| +    demuxer_->timeline_offset_ = timeline_offset;
 | 
|      event.RunAndWaitForStatus(PIPELINE_OK);
 | 
|    }
 | 
|  
 | 
| +  void InitializeDemuxerText(bool enable_text) {
 | 
| +    InitializeDemuxerWithTimelineOffset(enable_text, base::Time());
 | 
| +  }
 | 
| +
 | 
|    void InitializeDemuxer() {
 | 
|      InitializeDemuxerText(false);
 | 
|    }
 | 
| @@ -109,7 +115,9 @@ class FFmpegDemuxerTest : public testing::Test {
 | 
|    // |location| simply indicates where the call to this function was made.
 | 
|    // This makes it easier to track down where test failures occur.
 | 
|    void OnReadDone(const tracked_objects::Location& location,
 | 
| -                  int size, int64 timestampInMicroseconds,
 | 
| +                  int size,
 | 
| +                  int64 timestamp_us,
 | 
| +                  base::TimeDelta discard_front_padding,
 | 
|                    DemuxerStream::Status status,
 | 
|                    const scoped_refptr<DecoderBuffer>& buffer) {
 | 
|      std::string location_str;
 | 
| @@ -117,21 +125,39 @@ class FFmpegDemuxerTest : public testing::Test {
 | 
|      location_str += "\n";
 | 
|      SCOPED_TRACE(location_str);
 | 
|      EXPECT_EQ(status, DemuxerStream::kOk);
 | 
| -    OnReadDoneCalled(size, timestampInMicroseconds);
 | 
| +    OnReadDoneCalled(size, timestamp_us);
 | 
|      EXPECT_TRUE(buffer.get() != NULL);
 | 
|      EXPECT_EQ(size, buffer->data_size());
 | 
| -    EXPECT_EQ(base::TimeDelta::FromMicroseconds(timestampInMicroseconds),
 | 
| -              buffer->timestamp());
 | 
| -
 | 
| +    EXPECT_EQ(timestamp_us, buffer->timestamp().InMicroseconds());
 | 
| +    EXPECT_EQ(discard_front_padding, buffer->discard_padding().first);
 | 
|      DCHECK_EQ(&message_loop_, base::MessageLoop::current());
 | 
|      message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
 | 
|    }
 | 
|  
 | 
|    DemuxerStream::ReadCB NewReadCB(const tracked_objects::Location& location,
 | 
| -                                  int size, int64 timestampInMicroseconds) {
 | 
| -    EXPECT_CALL(*this, OnReadDoneCalled(size, timestampInMicroseconds));
 | 
| -    return base::Bind(&FFmpegDemuxerTest::OnReadDone, base::Unretained(this),
 | 
| -                      location, size, timestampInMicroseconds);
 | 
| +                                  int size,
 | 
| +                                  int64 timestamp_us) {
 | 
| +    EXPECT_CALL(*this, OnReadDoneCalled(size, timestamp_us));
 | 
| +    return base::Bind(&FFmpegDemuxerTest::OnReadDone,
 | 
| +                      base::Unretained(this),
 | 
| +                      location,
 | 
| +                      size,
 | 
| +                      timestamp_us,
 | 
| +                      base::TimeDelta());
 | 
| +  }
 | 
| +
 | 
| +  DemuxerStream::ReadCB NewReadCBWithCheckedDiscard(
 | 
| +      const tracked_objects::Location& location,
 | 
| +      int size,
 | 
| +      int64 timestamp_us,
 | 
| +      base::TimeDelta discard_front_padding) {
 | 
| +    EXPECT_CALL(*this, OnReadDoneCalled(size, timestamp_us));
 | 
| +    return base::Bind(&FFmpegDemuxerTest::OnReadDone,
 | 
| +                      base::Unretained(this),
 | 
| +                      location,
 | 
| +                      size,
 | 
| +                      timestamp_us,
 | 
| +                      discard_front_padding);
 | 
|    }
 | 
|  
 | 
|    // TODO(xhwang): This is a workaround of the issue that move-only parameters
 | 
| @@ -386,25 +412,102 @@ TEST_F(FFmpegDemuxerTest, Read_Text) {
 | 
|    message_loop_.Run();
 | 
|  }
 | 
|  
 | 
| -TEST_F(FFmpegDemuxerTest, Read_VideoNonZeroStart) {
 | 
| +TEST_F(FFmpegDemuxerTest, Read_VideoPositiveStartTime) {
 | 
| +  const int64 kTimelineOffsetMs = 1352550896000LL;
 | 
| +
 | 
|    // Test the start time is the first timestamp of the video and audio stream.
 | 
|    CreateDemuxer("nonzero-start-time.webm");
 | 
| -  InitializeDemuxer();
 | 
| +  InitializeDemuxerWithTimelineOffset(
 | 
| +      false, base::Time::FromJsTime(kTimelineOffsetMs));
 | 
|  
 | 
|    // Attempt a read from the video stream and run the message loop until done.
 | 
|    DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
 | 
|    DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
 | 
|  
 | 
| -  // Check first buffer in video stream.
 | 
| -  video->Read(NewReadCB(FROM_HERE, 5636, 400000));
 | 
| -  message_loop_.Run();
 | 
| +  const base::TimeDelta video_start_time =
 | 
| +      base::TimeDelta::FromMicroseconds(400000);
 | 
| +  const base::TimeDelta audio_start_time =
 | 
| +      base::TimeDelta::FromMicroseconds(396000);
 | 
| +
 | 
| +  // Run the test twice with a seek in between.
 | 
| +  for (int i = 0; i < 2; ++i) {
 | 
| +    // Check first buffer in video stream.  It should have been adjusted such
 | 
| +    // that it starts 400ms after the first audio buffer.
 | 
| +    video->Read(
 | 
| +        NewReadCB(FROM_HERE,
 | 
| +                  5636,
 | 
| +                  (video_start_time - audio_start_time).InMicroseconds()));
 | 
| +    message_loop_.Run();
 | 
|  
 | 
| -  // Check first buffer in audio stream.
 | 
| -  audio->Read(NewReadCB(FROM_HERE, 165, 396000));
 | 
| -  message_loop_.Run();
 | 
| +    // Since the audio buffer has a lower first timestamp, it should become
 | 
| +    // zero.
 | 
| +    audio->Read(NewReadCB(FROM_HERE, 165, 0));
 | 
| +    message_loop_.Run();
 | 
| +
 | 
| +    // Verify that the start time is equal to the lowest timestamp (ie the
 | 
| +    // audio).
 | 
| +    EXPECT_EQ(audio_start_time, demuxer_->start_time());
 | 
| +
 | 
| +    // Verify that the timeline offset has been adjusted by the start time.
 | 
| +    EXPECT_EQ(kTimelineOffsetMs + audio_start_time.InMilliseconds(),
 | 
| +              demuxer_->GetTimelineOffset().ToJavaTime());
 | 
| +
 | 
| +    // Seek back to the beginning and repeat the test.
 | 
| +    WaitableMessageLoopEvent event;
 | 
| +    demuxer_->Seek(base::TimeDelta(), event.GetPipelineStatusCB());
 | 
| +    event.RunAndWaitForStatus(PIPELINE_OK);
 | 
| +  }
 | 
| +}
 | 
|  
 | 
| -  // Verify that the start time is equal to the lowest timestamp (ie the audio).
 | 
| -  EXPECT_EQ(demuxer_->GetStartTime().InMicroseconds(), 396000);
 | 
| +TEST_F(FFmpegDemuxerTest, Read_AudioNoStartTime) {
 | 
| +  // FFmpeg does not set timestamps when demuxing wave files.  Ensure that the
 | 
| +  // demuxer sets a start time of zero in this case.
 | 
| +  CreateDemuxer("sfx_s24le.wav");
 | 
| +  InitializeDemuxer();
 | 
| +
 | 
| +  // Run the test twice with a seek in between.
 | 
| +  for (int i = 0; i < 2; ++i) {
 | 
| +    demuxer_->GetStream(DemuxerStream::AUDIO)
 | 
| +        ->Read(NewReadCB(FROM_HERE, 4095, 0));
 | 
| +    message_loop_.Run();
 | 
| +    EXPECT_EQ(base::TimeDelta(), demuxer_->start_time());
 | 
| +
 | 
| +    // Seek back to the beginning and repeat the test.
 | 
| +    WaitableMessageLoopEvent event;
 | 
| +    demuxer_->Seek(base::TimeDelta(), event.GetPipelineStatusCB());
 | 
| +    event.RunAndWaitForStatus(PIPELINE_OK);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST_F(FFmpegDemuxerTest, Read_AudioNegativeStartTimeAndOggDiscard) {
 | 
| +  // Many ogg files have negative starting timestamps, so ensure demuxing and
 | 
| +  // seeking work correctly with a negative start time.
 | 
| +  CreateDemuxer("bear.ogv");
 | 
| +  InitializeDemuxer();
 | 
| +
 | 
| +  // Run the test twice with a seek in between.
 | 
| +  for (int i = 0; i < 2; ++i) {
 | 
| +    demuxer_->GetStream(DemuxerStream::AUDIO)->Read(
 | 
| +        NewReadCBWithCheckedDiscard(FROM_HERE, 40, 0, kInfiniteDuration()));
 | 
| +    message_loop_.Run();
 | 
| +    demuxer_->GetStream(DemuxerStream::AUDIO)->Read(
 | 
| +        NewReadCBWithCheckedDiscard(FROM_HERE, 41, 2903, kInfiniteDuration()));
 | 
| +    message_loop_.Run();
 | 
| +    demuxer_->GetStream(DemuxerStream::AUDIO)->Read(NewReadCBWithCheckedDiscard(
 | 
| +        FROM_HERE, 173, 5805, base::TimeDelta::FromMicroseconds(10159)));
 | 
| +    message_loop_.Run();
 | 
| +
 | 
| +    demuxer_->GetStream(DemuxerStream::AUDIO)
 | 
| +        ->Read(NewReadCB(FROM_HERE, 148, 18866));
 | 
| +    message_loop_.Run();
 | 
| +    EXPECT_EQ(base::TimeDelta::FromMicroseconds(-15964),
 | 
| +              demuxer_->start_time());
 | 
| +
 | 
| +    // Seek back to the beginning and repeat the test.
 | 
| +    WaitableMessageLoopEvent event;
 | 
| +    demuxer_->Seek(base::TimeDelta(), event.GetPipelineStatusCB());
 | 
| +    event.RunAndWaitForStatus(PIPELINE_OK);
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  TEST_F(FFmpegDemuxerTest, Read_EndOfStream) {
 | 
| 
 |