Shaka Player Embedded
ffmpeg_decoder.cc
Go to the documentation of this file.
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
17 #include <glog/logging.h>
18 
19 #include <string>
20 #include <unordered_map>
21 
23 #include "src/media/media_utils.h"
24 #include "src/util/utils.h"
25 
26 namespace shaka {
27 namespace media {
28 namespace ffmpeg {
29 
30 namespace {
31 
32 #define LogError(code, extra_info) \
33  LOG(ERROR) << (*(extra_info) = \
34  std::string("Error from FFmpeg: ") + av_err2str(code))
35 #define ALLOC_ERROR_STR "Error allocating memory"
36 
37 const AVCodec* FindCodec(const std::string& codec_name) {
38 #ifdef ENABLE_HARDWARE_DECODE
39  const AVCodec* hybrid = nullptr;
40  const AVCodec* external = nullptr;
41  void* opaque = nullptr;
42  while (const AVCodec* codec = av_codec_iterate(&opaque)) {
43  if (avcodec_get_name(codec->id) == codec_name &&
44  av_codec_is_decoder(codec)) {
45  if (codec->capabilities & AV_CODEC_CAP_HARDWARE)
46  return codec;
47 
48  if (codec->capabilities & AV_CODEC_CAP_HYBRID) {
49  // Keep the hybrid as a fallback, but try to find a hardware-only one.
50  hybrid = codec;
51  } else if (codec->wrapper_name) {
52  // This is an external codec, which may be provided by the OS. Fallback
53  // to this if nothing else.
54  external = codec;
55  }
56  }
57  }
58  if (hybrid)
59  return hybrid;
60  if (external)
61  return external;
62 #endif
63  return avcodec_find_decoder_by_name(codec_name.c_str());
64 }
65 
66 std::string GetCodecFromMime(const std::string& mime) {
67  std::unordered_map<std::string, std::string> params;
68  if (!ParseMimeType(mime, nullptr, nullptr, &params))
69  return "";
70  auto it = params.find(kCodecMimeParam);
71  return it != params.end() ? it->second : "";
72 }
73 
74 } // namespace
75 
77  : mutex_("FFmpegDecoder"),
78  decoder_ctx_(nullptr),
79  received_frame_(nullptr),
80 #ifdef ENABLE_HARDWARE_DECODE
81  hw_device_ctx_(nullptr),
82  hw_pix_fmt_(AV_PIX_FMT_NONE),
83 #endif
84  prev_timestamp_offset_(0) {
85 }
86 
88  // It is safe if these fields are nullptr.
89  avcodec_free_context(&decoder_ctx_);
90  av_frame_free(&received_frame_);
91 #ifdef ENABLE_HARDWARE_DECODE
92  av_buffer_unref(&hw_device_ctx_);
93 #endif
94 }
95 
97  const MediaDecodingConfiguration& config) const {
99  const bool has_video = !config.video.content_type.empty();
100  const bool has_audio = !config.audio.content_type.empty();
101  if (has_audio == has_video || config.type != MediaDecodingType::MediaSource)
102  return ret;
103 
104  const std::string codec = GetCodecFromMime(
105  has_video ? config.video.content_type : config.audio.content_type);
106  // If codec isn't given, assume supported but not hardware accelerated.
107  auto* c = FindCodec(NormalizeCodec(codec));
108  ret.supported = codec.empty() || c != nullptr;
109  ret.power_efficient = ret.smooth = c && c->wrapper_name;
110  return ret;
111 }
112 
114  std::unique_lock<Mutex> lock(mutex_);
115  avcodec_free_context(&decoder_ctx_);
116 }
117 
119  std::shared_ptr<EncodedFrame> input, const eme::Implementation* eme,
120  std::vector<std::shared_ptr<DecodedFrame>>* frames,
121  std::string* extra_info) {
122  std::unique_lock<Mutex> lock(mutex_);
123  if (!input && !decoder_ctx_) {
124  // If there isn't a decoder, there is nothing to flush.
125  return MediaStatus::Success;
126  }
127 
128  if (input) {
129  if (!decoder_ctx_ || input->stream_info != decoder_stream_info_) {
130  VLOG(1) << "Reconfiguring decoder";
131  // Flush the old decoder to get any existing frames.
132  if (decoder_ctx_) {
133  const int send_code = avcodec_send_packet(decoder_ctx_, nullptr);
134  if (send_code != 0) {
135  LogError(send_code, extra_info);
137  }
138  if (!ReadFromDecoder(decoder_stream_info_, nullptr, frames, extra_info))
140  }
141 
142  if (!InitializeDecoder(input->stream_info, true, extra_info))
144  }
145 
146  prev_timestamp_offset_ = input->timestamp_offset;
147  }
148 
149 
150  // If the encoded frame is encrypted, decrypt it first.
151  AVPacket packet{};
152  util::Finally free_decrypted_packet(std::bind(&av_packet_unref, &packet));
153  if (input && input->encryption_info) {
154  if (!eme) {
155  LOG(WARNING) << (*extra_info = "No CDM given for encrypted frame");
157  }
158 
159  int code = av_new_packet(&packet, input->data_size);
160  if (code < 0) {
161  LogError(code, extra_info);
163  }
164 
165  MediaStatus decrypt_status = input->Decrypt(eme, packet.data);
166  if (decrypt_status == MediaStatus::KeyNotFound)
168  if (decrypt_status != MediaStatus::Success) {
169  *extra_info = "CDM returned error while decrypting frame";
171  }
172  } else if (input) {
173  const double timescale = input->stream_info->time_scale;
174  packet.pts = static_cast<int64_t>(input->pts / timescale);
175  packet.dts = static_cast<int64_t>(input->dts / timescale);
176  packet.data = const_cast<uint8_t*>(input->data);
177  packet.size = input->data_size;
178  }
179 
180  bool sent_frame = false;
181  while (!sent_frame) {
182  // If we get EAGAIN, we should read some frames and try to send again.
183  const int send_code = avcodec_send_packet(decoder_ctx_, &packet);
184  if (send_code == 0) {
185  sent_frame = true;
186  } else if (send_code == AVERROR_EOF) {
187  // If we get EOF, this is either a flush or we are closing. Either way,
188  // stop. If this is a flush, we can't reuse the decoder, so reset it.
189  avcodec_free_context(&decoder_ctx_);
190  break;
191  } else if (send_code != AVERROR(EAGAIN)) {
192  LogError(send_code, extra_info);
194  }
195 
196  auto stream_info = input ? input->stream_info : decoder_stream_info_;
197  if (!ReadFromDecoder(stream_info, input, frames, extra_info))
199  }
200 
201  return MediaStatus::Success;
202 }
203 
204 #ifdef ENABLE_HARDWARE_DECODE
205 AVPixelFormat FFmpegDecoder::GetPixelFormat(AVCodecContext* ctx,
206  const AVPixelFormat* formats) {
207  AVPixelFormat desired =
208  reinterpret_cast<FFmpegDecoder*>(ctx->opaque)->hw_pix_fmt_;
209  for (size_t i = 0; formats[i] != AV_PIX_FMT_NONE; i++) {
210  if (formats[i] == desired)
211  return formats[i];
212  }
213 # ifdef FORCE_HARDWARE_DECODE
214  LOG(DFATAL) << "Hardware pixel format is unsupported.";
215  return AV_PIX_FMT_NONE;
216 # else
217  LOG(ERROR) << "Hardware pixel format is unsupported, may be falling back "
218  "to software decoder.";
219  return formats[0];
220 # endif
221 }
222 #endif
223 
224 bool FFmpegDecoder::InitializeDecoder(std::shared_ptr<const StreamInfo> info,
225  bool allow_hardware,
226  std::string* extra_info) {
227  const AVCodec* decoder =
228  allow_hardware
229  ? FindCodec(NormalizeCodec(info->codec))
230  : avcodec_find_decoder_by_name(NormalizeCodec(info->codec).c_str());
231  CHECK(decoder) << "Should have checked support already";
232 #ifdef ENABLE_HARDWARE_DECODE
233  AVHWDeviceType hw_type = AV_HWDEVICE_TYPE_NONE;
234  hw_pix_fmt_ = AV_PIX_FMT_NONE;
235  if (allow_hardware) {
236  for (int i = 0;; i++) {
237  const AVCodecHWConfig* config = avcodec_get_hw_config(decoder, i);
238  if (!config) {
239 # ifdef FORCE_HARDWARE_DECODE
240  if (!decoder->wrapper_name) {
241  *extra_info =
242  "No hardware-accelerators available for codec: " + info->codec;
243  LOG(DFATAL) << *extra_info;
244  return false;
245  }
246 # endif
247  LOG(INFO) << "No hardware-accelerators available, using decoder: "
248  << decoder->name;
249  break;
250  }
251 
252  if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) {
253  LOG(INFO) << "Using decoder: " << decoder->name
254  << ", with hardware accelerator: "
255  << av_hwdevice_get_type_name(config->device_type);
256  hw_type = config->device_type;
257  hw_pix_fmt_ = config->pix_fmt;
258  break;
259  }
260  }
261  }
262 #endif
263 
264  avcodec_free_context(&decoder_ctx_);
265  decoder_ctx_ = avcodec_alloc_context3(decoder);
266  if (!decoder_ctx_) {
267  *extra_info = ALLOC_ERROR_STR;
268  return false;
269  }
270 
271  if (!received_frame_) {
272  received_frame_ = av_frame_alloc();
273  if (!received_frame_) {
274  *extra_info = ALLOC_ERROR_STR;
275  return false;
276  }
277  }
278 
279  decoder_ctx_->thread_count = 0; // Default is 1; 0 means auto-detect.
280  decoder_ctx_->opaque = this;
281  decoder_ctx_->pkt_timebase = {.num = info->time_scale.numerator,
282  .den = info->time_scale.denominator};
283 
284  if (!info->extra_data.empty()) {
285  av_freep(&decoder_ctx_->extradata);
286  decoder_ctx_->extradata = reinterpret_cast<uint8_t*>(
287  av_mallocz(info->extra_data.size() + AV_INPUT_BUFFER_PADDING_SIZE));
288  if (!decoder_ctx_->extradata) {
289  *extra_info = ALLOC_ERROR_STR;
290  return false;
291  }
292  memcpy(decoder_ctx_->extradata, info->extra_data.data(),
293  info->extra_data.size());
294  decoder_ctx_->extradata_size = info->extra_data.size();
295  }
296 
297 #ifdef ENABLE_HARDWARE_DECODE
298  // If using a hardware accelerator, initialize it now.
299  av_buffer_unref(&hw_device_ctx_);
300  if (allow_hardware && hw_type != AV_HWDEVICE_TYPE_NONE) {
301  const int hw_device_code =
302  av_hwdevice_ctx_create(&hw_device_ctx_, hw_type, nullptr, nullptr, 0);
303  if (hw_device_code < 0) {
304  LogError(hw_device_code, extra_info);
305  return false;
306  }
307  decoder_ctx_->get_format = &GetPixelFormat;
308  decoder_ctx_->hw_device_ctx = av_buffer_ref(hw_device_ctx_);
309  }
310 #endif
311 
312  const int open_code = avcodec_open2(decoder_ctx_, decoder, nullptr);
313  if (open_code < 0) {
314  if (open_code == AVERROR(ENOMEM)) {
315  *extra_info = ALLOC_ERROR_STR;
316  return false;
317  }
318 #if defined(ENABLE_HARDWARE_DECODE) && !defined(FORCE_HARDWARE_DECODE)
319  if (allow_hardware) {
320  LOG(WARNING) << "Failed to initialize hardware decoder, falling back "
321  "to software.";
322  return InitializeDecoder(info, false, extra_info);
323  }
324 #endif
325 
326  LogError(open_code, extra_info);
327  return false;
328  }
329 
330  decoder_stream_info_ = info;
331  return true;
332 }
333 
334 bool FFmpegDecoder::ReadFromDecoder(
335  std::shared_ptr<const StreamInfo> stream_info,
336  std::shared_ptr<EncodedFrame> input,
337  std::vector<std::shared_ptr<DecodedFrame>>* decoded,
338  std::string* extra_info) {
339  while (true) {
340  const int code = avcodec_receive_frame(decoder_ctx_, received_frame_);
341  if (code == AVERROR(EAGAIN) || code == AVERROR_EOF)
342  return true;
343  if (code < 0) {
344  LogError(code, extra_info);
345  return false;
346  }
347 
348  const double timescale = stream_info->time_scale;
349  const int64_t timestamp = received_frame_->best_effort_timestamp;
350  const double offset =
351  input ? input->timestamp_offset : prev_timestamp_offset_;
352  const double time = input && timestamp == AV_NOPTS_VALUE
353  ? input->pts
354  : timestamp * timescale + offset;
355  auto* new_frame = FFmpegDecodedFrame::CreateFrame(
356  stream_info, received_frame_, time, input ? input->duration : 0);
357  if (!new_frame) {
358  *extra_info = ALLOC_ERROR_STR;
359  return false;
360  }
361  decoded->emplace_back(new_frame);
362  }
363 }
364 
365 } // namespace ffmpeg
366 } // namespace media
367 } // namespace shaka
bool ParseMimeType(const std::string &source, std::string *type, std::string *subtype, std::unordered_map< std::string, std::string > *params)
Definition: media_utils.cc:62
static FFmpegDecodedFrame * CreateFrame(std::shared_ptr< const StreamInfo > stream, AVFrame *frame, double time, double duration)
std::string NormalizeCodec(const std::string &codec)
Definition: media_utils.cc:135
#define ALLOC_ERROR_STR
MediaCapabilitiesInfo DecodingInfo(const MediaDecodingConfiguration &config) const override
constexpr const char * kCodecMimeParam
Definition: media_utils.h:33
std::list< std::shared_ptr< BaseFrame > > frames
Definition: streams.cc:128
#define LogError(code, extra_info)
MediaStatus Decode(std::shared_ptr< EncodedFrame > input, const eme::Implementation *eme, std::vector< std::shared_ptr< DecodedFrame >> *frames, std::string *extra_info) override