ffmpeg_glue.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. #include "media/filters/ffmpeg_glue.h"
  5. #include "base/check_op.h"
  6. #include "base/metrics/histogram_functions.h"
  7. #include "base/notreached.h"
  8. #include "media/base/container_names.h"
  9. #include "media/ffmpeg/ffmpeg_common.h"
  10. namespace media {
  11. // Internal buffer size used by AVIO for reading.
  12. // TODO(dalecurtis): Experiment with this buffer size and measure impact on
  13. // performance. Currently we want to use 32kb to preserve existing behavior
  14. // with the previous URLProtocol based approach.
  15. enum { kBufferSize = 32 * 1024 };
  16. static int AVIOReadOperation(void* opaque, uint8_t* buf, int buf_size) {
  17. return reinterpret_cast<FFmpegURLProtocol*>(opaque)->Read(buf_size, buf);
  18. }
  19. static int64_t AVIOSeekOperation(void* opaque, int64_t offset, int whence) {
  20. FFmpegURLProtocol* protocol = reinterpret_cast<FFmpegURLProtocol*>(opaque);
  21. int64_t new_offset = AVERROR(EIO);
  22. switch (whence) {
  23. case SEEK_SET:
  24. if (protocol->SetPosition(offset))
  25. protocol->GetPosition(&new_offset);
  26. break;
  27. case SEEK_CUR:
  28. int64_t pos;
  29. if (!protocol->GetPosition(&pos))
  30. break;
  31. if (protocol->SetPosition(pos + offset))
  32. protocol->GetPosition(&new_offset);
  33. break;
  34. case SEEK_END:
  35. int64_t size;
  36. if (!protocol->GetSize(&size))
  37. break;
  38. if (protocol->SetPosition(size + offset))
  39. protocol->GetPosition(&new_offset);
  40. break;
  41. case AVSEEK_SIZE:
  42. protocol->GetSize(&new_offset);
  43. break;
  44. default:
  45. NOTREACHED();
  46. }
  47. return new_offset;
  48. }
  49. static void LogContainer(bool is_local_file,
  50. container_names::MediaContainerName container) {
  51. base::UmaHistogramSparse("Media.DetectedContainer", container);
  52. if (is_local_file)
  53. base::UmaHistogramSparse("Media.DetectedContainer.Local", container);
  54. }
  55. FFmpegGlue::FFmpegGlue(FFmpegURLProtocol* protocol) {
  56. // Initialize an AVIOContext using our custom read and seek operations. Don't
  57. // keep pointers to the buffer since FFmpeg may reallocate it on the fly. It
  58. // will be cleaned up
  59. format_context_ = avformat_alloc_context();
  60. avio_context_.reset(avio_alloc_context(
  61. static_cast<unsigned char*>(av_malloc(kBufferSize)), kBufferSize, 0,
  62. protocol, &AVIOReadOperation, nullptr, &AVIOSeekOperation));
  63. // Ensure FFmpeg only tries to seek on resources we know to be seekable.
  64. avio_context_->seekable =
  65. protocol->IsStreaming() ? 0 : AVIO_SEEKABLE_NORMAL;
  66. // Ensure writing is disabled.
  67. avio_context_->write_flag = 0;
  68. // Tell the format context about our custom IO context. avformat_open_input()
  69. // will set the AVFMT_FLAG_CUSTOM_IO flag for us, but do so here to ensure an
  70. // early error state doesn't cause FFmpeg to free our resources in error.
  71. format_context_->flags |= AVFMT_FLAG_CUSTOM_IO;
  72. // Enable fast, but inaccurate seeks for MP3.
  73. format_context_->flags |= AVFMT_FLAG_FAST_SEEK;
  74. // Ensures format parsing errors will bail out. From an audit on 11/2017, all
  75. // instances were real failures. Solves bugs like http://crbug.com/710791.
  76. format_context_->error_recognition |= AV_EF_EXPLODE;
  77. format_context_->pb = avio_context_.get();
  78. }
  79. bool FFmpegGlue::OpenContext(bool is_local_file) {
  80. DCHECK(!open_called_) << "OpenContext() shouldn't be called twice.";
  81. // If avformat_open_input() is called we have to take a slightly different
  82. // destruction path to avoid double frees.
  83. open_called_ = true;
  84. // By passing nullptr for the filename (second parameter) we are telling
  85. // FFmpeg to use the AVIO context we setup from the AVFormatContext structure.
  86. const int ret =
  87. avformat_open_input(&format_context_, nullptr, nullptr, nullptr);
  88. // If FFmpeg can't identify the file, read the first 8k and attempt to guess
  89. // at the container type ourselves. This way we can track emergent formats.
  90. // Only try on AVERROR_INVALIDDATA to avoid running after I/O errors.
  91. if (ret == AVERROR_INVALIDDATA) {
  92. std::vector<uint8_t> buffer(8192);
  93. const int64_t pos = AVIOSeekOperation(avio_context_->opaque, 0, SEEK_SET);
  94. if (pos < 0)
  95. return false;
  96. const int num_read =
  97. AVIOReadOperation(avio_context_->opaque, buffer.data(), buffer.size());
  98. if (num_read < container_names::kMinimumContainerSize)
  99. return false;
  100. container_ = container_names::DetermineContainer(buffer.data(), num_read);
  101. LogContainer(is_local_file, container_);
  102. detected_hls_ =
  103. container_ == container_names::MediaContainerName::CONTAINER_HLS;
  104. return false;
  105. } else if (ret < 0) {
  106. return false;
  107. }
  108. // Rely on ffmpeg's parsing if we're able to succesfully open the file.
  109. if (strcmp(format_context_->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0)
  110. container_ = container_names::CONTAINER_MOV;
  111. else if (strcmp(format_context_->iformat->name, "flac") == 0)
  112. container_ = container_names::CONTAINER_FLAC;
  113. else if (strcmp(format_context_->iformat->name, "matroska,webm") == 0)
  114. container_ = container_names::CONTAINER_WEBM;
  115. else if (strcmp(format_context_->iformat->name, "ogg") == 0)
  116. container_ = container_names::CONTAINER_OGG;
  117. else if (strcmp(format_context_->iformat->name, "wav") == 0)
  118. container_ = container_names::CONTAINER_WAV;
  119. else if (strcmp(format_context_->iformat->name, "aac") == 0)
  120. container_ = container_names::CONTAINER_AAC;
  121. else if (strcmp(format_context_->iformat->name, "mp3") == 0)
  122. container_ = container_names::CONTAINER_MP3;
  123. else if (strcmp(format_context_->iformat->name, "amr") == 0)
  124. container_ = container_names::CONTAINER_AMR;
  125. else if (strcmp(format_context_->iformat->name, "avi") == 0)
  126. container_ = container_names::CONTAINER_AVI;
  127. DCHECK_NE(container_, container_names::CONTAINER_UNKNOWN);
  128. LogContainer(is_local_file, container_);
  129. return true;
  130. }
  131. FFmpegGlue::~FFmpegGlue() {
  132. // In the event of avformat_open_input() failure, FFmpeg may sometimes free
  133. // our AVFormatContext behind the scenes, but leave the buffer alive. It will
  134. // helpfully set |format_context_| to nullptr in this case.
  135. if (!format_context_) {
  136. av_free(avio_context_->buffer);
  137. return;
  138. }
  139. // If avformat_open_input() hasn't been called, we should simply free the
  140. // AVFormatContext and buffer instead of using avformat_close_input().
  141. if (!open_called_) {
  142. avformat_free_context(format_context_);
  143. av_free(avio_context_->buffer);
  144. return;
  145. }
  146. avformat_close_input(&format_context_);
  147. av_free(avio_context_->buffer);
  148. }
  149. } // namespace media