Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / media / filters / ffmpeg_demuxer.cc
index d2267f3..061d631 100644 (file)
 
 namespace media {
 
+static base::Time ExtractTimelineOffset(AVFormatContext* format_context) {
+  if (strstr(format_context->iformat->name, "webm") ||
+      strstr(format_context->iformat->name, "matroska")) {
+    const AVDictionaryEntry* entry =
+        av_dict_get(format_context->metadata, "creation_time", NULL, 0);
+
+    base::Time timeline_offset;
+    if (entry != NULL && entry->value != NULL &&
+        FFmpegUTCDateToTime(entry->value, &timeline_offset)) {
+      return timeline_offset;
+    }
+  }
+
+  return base::Time();
+}
+
+static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate) {
+  return base::TimeDelta::FromMicroseconds(
+      frames * base::Time::kMicrosecondsPerSecond / sample_rate);
+}
+
 //
 // FFmpegDemuxerStream
 //
@@ -74,10 +95,12 @@ FFmpegDemuxerStream::FFmpegDemuxerStream(
   // Calculate the duration.
   duration_ = ConvertStreamTimestamp(stream->time_base, stream->duration);
 
+#if defined(USE_PROPRIETARY_CODECS)
   if (stream_->codec->codec_id == AV_CODEC_ID_H264) {
     bitstream_converter_.reset(
         new FFmpegH264ToAnnexBBitstreamConverter(stream_->codec));
   }
+#endif
 
   if (is_encrypted) {
     AVDictionaryEntry* key = av_dict_get(stream->metadata, "enc_key_id", NULL,
@@ -106,11 +129,13 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
     return;
   }
 
+#if defined(USE_PROPRIETARY_CODECS)
   // Convert the packet if there is a bitstream filter.
   if (packet->data && bitstream_converter_enabled_ &&
       !bitstream_converter_->ConvertPacket(packet.get())) {
     LOG(ERROR) << "Format conversion failed.";
   }
+#endif
 
   // Get side data if any. For now, the only type of side_data is VP8 Alpha. We
   // keep this generic so that other side_data types in the future can be
@@ -173,20 +198,28 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
     }
 
     int skip_samples_size = 0;
-    uint8* skip_samples = av_packet_get_side_data(packet.get(),
-                                                  AV_PKT_DATA_SKIP_SAMPLES,
-                                                  &skip_samples_size);
+    const uint32* skip_samples_ptr =
+        reinterpret_cast<const uint32*>(av_packet_get_side_data(
+            packet.get(), AV_PKT_DATA_SKIP_SAMPLES, &skip_samples_size));
     const int kSkipSamplesValidSize = 10;
-    const int kSkipSamplesOffset = 4;
+    const int kSkipEndSamplesOffset = 1;
     if (skip_samples_size >= kSkipSamplesValidSize) {
-      int discard_padding_samples = base::ByteSwapToLE32(
-          *(reinterpret_cast<const uint32*>(skip_samples +
-                                            kSkipSamplesOffset)));
-      // TODO(vigneshv): Change decoder buffer to use number of samples so that
-      // this conversion can be avoided.
-      buffer->set_discard_padding(base::TimeDelta::FromMicroseconds(
-          discard_padding_samples * 1000000.0 /
-          audio_decoder_config().samples_per_second()));
+      // Because FFmpeg rolls codec delay and skip samples into one we can only
+      // allow front discard padding on the first buffer.  Otherwise the discard
+      // helper can't figure out which data to discard.  See AudioDiscardHelper.
+      int discard_front_samples = base::ByteSwapToLE32(*skip_samples_ptr);
+      if (last_packet_timestamp_ != kNoTimestamp()) {
+        DLOG(ERROR) << "Skip samples are only allowed for the first packet.";
+        discard_front_samples = 0;
+      }
+
+      const int discard_end_samples =
+          base::ByteSwapToLE32(*(skip_samples_ptr + kSkipEndSamplesOffset));
+      const int samples_per_second =
+          audio_decoder_config().samples_per_second();
+      buffer->set_discard_padding(std::make_pair(
+          FramesToTimeDelta(discard_front_samples, samples_per_second),
+          FramesToTimeDelta(discard_end_samples, samples_per_second)));
     }
 
     if (decrypt_config)
@@ -264,8 +297,13 @@ void FFmpegDemuxerStream::Read(const ReadCB& read_cb) {
 
 void FFmpegDemuxerStream::EnableBitstreamConverter() {
   DCHECK(task_runner_->BelongsToCurrentThread());
+
+#if defined(USE_PROPRIETARY_CODECS)
   CHECK(bitstream_converter_.get());
   bitstream_converter_enabled_ = true;
+#else
+  NOTREACHED() << "Proprietary codecs not enabled.";
+#endif
 }
 
 bool FFmpegDemuxerStream::SupportsConfigChanges() { return false; }
@@ -378,7 +416,7 @@ FFmpegDemuxer::FFmpegDemuxer(
       media_log_(media_log),
       bitrate_(0),
       start_time_(kNoTimestamp()),
-      audio_disabled_(false),
+      liveness_(LIVENESS_UNKNOWN),
       text_enabled_(false),
       duration_known_(false),
       need_key_cb_(need_key_cb),
@@ -426,17 +464,6 @@ void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
           &FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr(), cb));
 }
 
-void FFmpegDemuxer::OnAudioRendererDisabled() {
-  DCHECK(task_runner_->BelongsToCurrentThread());
-  audio_disabled_ = true;
-  StreamVector::iterator iter;
-  for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
-    if (*iter && (*iter)->type() == DemuxerStream::AUDIO) {
-      (*iter)->Stop();
-    }
-  }
-}
-
 void FFmpegDemuxer::Initialize(DemuxerHost* host,
                                const PipelineStatusCB& status_cb,
                                bool enable_text_tracks) {
@@ -444,10 +471,6 @@ void FFmpegDemuxer::Initialize(DemuxerHost* host,
   host_ = host;
   text_enabled_ = enable_text_tracks;
 
-  // TODO(scherkus): DataSource should have a host by this point,
-  // see http://crbug.com/122071
-  data_source_->set_host(host);
-
   url_protocol_.reset(new BlockingUrlProtocol(data_source_, BindToCurrentLoop(
       base::Bind(&FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))));
   glue_.reset(new FFmpegGlue(url_protocol_.get()));
@@ -490,6 +513,15 @@ base::TimeDelta FFmpegDemuxer::GetStartTime() const {
   return start_time_;
 }
 
+base::Time FFmpegDemuxer::GetTimelineOffset() const {
+  return timeline_offset_;
+}
+
+Demuxer::Liveness FFmpegDemuxer::GetLiveness() const {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  return liveness_;
+}
+
 void FFmpegDemuxer::AddTextStreams() {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
@@ -678,6 +710,16 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
   if (strcmp(format_context->iformat->name, "avi") == 0)
     format_context->flags |= AVFMT_FLAG_GENPTS;
 
+  timeline_offset_ = ExtractTimelineOffset(format_context);
+
+  if (max_duration == kInfiniteDuration() && !timeline_offset_.is_null()) {
+    liveness_ = LIVENESS_LIVE;
+  } else if (max_duration != kInfiniteDuration()) {
+    liveness_ = LIVENESS_RECORDED;
+  } else {
+    liveness_ = LIVENESS_UNKNOWN;
+  }
+
   // Good to go: set the duration and bitrate and notify we're done
   // initializing.
   host_->SetDuration(max_duration);
@@ -852,10 +894,7 @@ void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) {
   // Defend against ffmpeg giving us a bad stream index.
   if (packet->stream_index >= 0 &&
       packet->stream_index < static_cast<int>(streams_.size()) &&
-      streams_[packet->stream_index] &&
-      (!audio_disabled_ ||
-       streams_[packet->stream_index]->type() != DemuxerStream::AUDIO)) {
-
+      streams_[packet->stream_index]) {
     // TODO(scherkus): Fix demuxing upstream to never return packets w/o data
     // when av_read_frame() returns success code. See bug comment for ideas:
     //
@@ -948,10 +987,8 @@ void FFmpegDemuxer::StreamHasEnded() {
   DCHECK(task_runner_->BelongsToCurrentThread());
   StreamVector::iterator iter;
   for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
-    if (!*iter ||
-        (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) {
+    if (!*iter)
       continue;
-    }
     (*iter)->SetEndOfStream();
   }
 }
@@ -971,8 +1008,7 @@ void FFmpegDemuxer::NotifyCapacityAvailable() {
 void FFmpegDemuxer::NotifyBufferingChanged() {
   DCHECK(task_runner_->BelongsToCurrentThread());
   Ranges<base::TimeDelta> buffered;
-  FFmpegDemuxerStream* audio =
-      audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO);
+  FFmpegDemuxerStream* audio = GetFFmpegStream(DemuxerStream::AUDIO);
   FFmpegDemuxerStream* video = GetFFmpegStream(DemuxerStream::VIDEO);
   if (audio && video) {
     buffered = audio->GetBufferedRanges().IntersectionWith(