| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/filters/media_file_checker.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <map> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/time/time.h" |
| #include "media/ffmpeg/ffmpeg_common.h" |
| #include "media/ffmpeg/ffmpeg_decoding_loop.h" |
| #include "media/ffmpeg/ffmpeg_deleters.h" |
| #include "media/filters/blocking_url_protocol.h" |
| #include "media/filters/ffmpeg_glue.h" |
| #include "media/filters/file_data_source.h" |
| |
| namespace media { |
| |
| static const int64_t kMaxCheckTimeInSeconds = 5; |
| |
| static void OnError(bool* called) { |
| *called = false; |
| } |
| |
| static bool DoNothingWithFrame(AVFrame* frame) { |
| return true; |
| } |
| |
| struct Decoder { |
| std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> context; |
| std::unique_ptr<FFmpegDecodingLoop> loop; |
| }; |
| |
| MediaFileChecker::MediaFileChecker(base::File file) : file_(std::move(file)) {} |
| |
| MediaFileChecker::~MediaFileChecker() = default; |
| |
| bool MediaFileChecker::Start(base::TimeDelta check_time) { |
| media::FileDataSource source(std::move(file_)); |
| bool read_ok = true; |
| media::BlockingUrlProtocol protocol(&source, base::Bind(&OnError, &read_ok)); |
| media::FFmpegGlue glue(&protocol); |
| AVFormatContext* format_context = glue.format_context(); |
| |
| if (!glue.OpenContext()) |
| return false; |
| |
| if (avformat_find_stream_info(format_context, NULL) < 0) |
| return false; |
| |
| // Remember the codec context for any decodable audio or video streams. |
| bool found_streams = false; |
| std::vector<Decoder> stream_contexts(format_context->nb_streams); |
| for (size_t i = 0; i < format_context->nb_streams; ++i) { |
| AVCodecParameters* cp = format_context->streams[i]->codecpar; |
| |
| if (cp->codec_type == AVMEDIA_TYPE_AUDIO || |
| cp->codec_type == AVMEDIA_TYPE_VIDEO) { |
| auto context = AVStreamToAVCodecContext(format_context->streams[i]); |
| if (!context) |
| continue; |
| AVCodec* codec = avcodec_find_decoder(cp->codec_id); |
| if (codec && avcodec_open2(context.get(), codec, nullptr) >= 0) { |
| auto loop = std::make_unique<FFmpegDecodingLoop>(context.get()); |
| stream_contexts[i] = {std::move(context), std::move(loop)}; |
| found_streams = true; |
| } |
| } |
| } |
| |
| if (!found_streams) |
| return false; |
| |
| AVPacket packet; |
| int result = 0; |
| |
| auto do_nothing_cb = base::BindRepeating(&DoNothingWithFrame); |
| const base::TimeTicks deadline = |
| base::TimeTicks::Now() + |
| std::min(check_time, |
| base::TimeDelta::FromSeconds(kMaxCheckTimeInSeconds)); |
| do { |
| result = av_read_frame(glue.format_context(), &packet); |
| if (result < 0) |
| break; |
| |
| auto& decoder = stream_contexts[packet.stream_index]; |
| if (decoder.loop) { |
| result = decoder.loop->DecodePacket(&packet, do_nothing_cb) == |
| FFmpegDecodingLoop::DecodeStatus::kOkay |
| ? 0 |
| : -1; |
| } |
| |
| av_packet_unref(&packet); |
| } while (base::TimeTicks::Now() < deadline && read_ok && result >= 0); |
| |
| stream_contexts.clear(); |
| return read_ok && (result == AVERROR_EOF || result >= 0); |
| } |
| |
| } // namespace media |