chromium/chrome/services/media_gallery_util/video_thumbnail_parser.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/services/media_gallery_util/video_thumbnail_parser.h"

#include <optional>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/task/bind_post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/services/media_gallery_util/ipc_data_source.h"
#include "media/base/media_util.h"
#include "media/base/supported_types.h"
#include "media/base/video_codecs.h"
#include "media/base/video_thumbnail_decoder.h"
#include "media/filters/android/video_frame_extractor.h"
#include "media/media_buildflags.h"

#if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
#include "media/filters/ffmpeg_video_decoder.h"
#endif

#if BUILDFLAG(ENABLE_LIBVPX)
#include "media/filters/vpx_video_decoder.h"
#endif

#if BUILDFLAG(ENABLE_AV1_DECODER)
#include "media/filters/dav1d_video_decoder.h"
#endif

namespace {

// Return the video frame back to browser process. A valid |config| is
// needed for deserialization.
void OnSoftwareVideoFrameDecoded(
    std::unique_ptr<media::MediaLog>,
    std::unique_ptr<media::VideoThumbnailDecoder>,
    MediaParser::ExtractVideoFrameCallback video_frame_callback,
    const media::VideoDecoderConfig& config,
    scoped_refptr<media::VideoFrame> frame) {
  DCHECK(video_frame_callback);

  if (!frame) {
    std::move(video_frame_callback).Run(nullptr);
    return;
  }

  std::move(video_frame_callback)
      .Run(chrome::mojom::ExtractVideoFrameResult::New(
          chrome::mojom::VideoFrameData::NewDecodedFrame(std::move(frame)),
          config));
}

void OnEncodedVideoFrameExtracted(
    std::unique_ptr<media::VideoFrameExtractor> video_frame_extractor,
    MediaParser::ExtractVideoFrameCallback video_frame_callback,
    bool success,
    std::vector<uint8_t> data,
    const media::VideoDecoderConfig& config) {
  if (!success || data.empty()) {
    std::move(video_frame_callback).Run(nullptr);
    return;
  }

  std::unique_ptr<media::MediaLog> log;
  std::unique_ptr<media::VideoDecoder> software_decoder;
  if (media::IsBuiltInVideoCodec(config.codec())) {
    switch (config.codec()) {
      case media::VideoCodec::kH264:
#if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
        log = std::make_unique<media::NullMediaLog>();
        software_decoder =
            std::make_unique<media::FFmpegVideoDecoder>(log.get());
        break;
#else
        // IsBuiltInVideoCodec(H264) should never return true if
        // ENABLE_FFMPEG_VIDEO_DECODERS is false.
        NOTREACHED();
#endif
      case media::VideoCodec::kVP8:
      case media::VideoCodec::kVP9:
#if BUILDFLAG(ENABLE_LIBVPX)
        software_decoder = std::make_unique<media::VpxVideoDecoder>();
        break;
#else
        // IsBuiltInVideoCodec(VP8|VP9) should never return true if
        // ENABLE_FFMPEG_VIDEO_DECODERS is false.
        NOTREACHED();
#endif
      case media::VideoCodec::kAV1:
#if BUILDFLAG(ENABLE_AV1_DECODER)
        software_decoder = std::make_unique<media::Dav1dVideoDecoder>(
            std::make_unique<media::NullMediaLog>());
        break;
#else
        // IsBuiltInVideoCodec(AV1) should never return true if
        // ENABLE_FFMPEG_VIDEO_DECODERS is false.
        NOTREACHED();
#endif

      default:
        std::move(video_frame_callback).Run(nullptr);
        return;
    }
  } else if (config.codec() == media::VideoCodec::kH264 ||
             config.codec() == media::VideoCodec::kHEVC) {
    std::move(video_frame_callback)
        .Run(chrome::mojom::ExtractVideoFrameResult::New(
            chrome::mojom::VideoFrameData::NewEncodedData(std::move(data)),
            config));
    return;
  } else {
    std::move(video_frame_callback).Run(nullptr);
    return;
  }

  auto thumbnail_decoder = std::make_unique<media::VideoThumbnailDecoder>(
      std::move(software_decoder), config, std::move(data));

  auto* const thumbnail_decoder_ptr = thumbnail_decoder.get();
  thumbnail_decoder_ptr->Start(base::BindOnce(
      &OnSoftwareVideoFrameDecoded, std::move(log),
      std::move(thumbnail_decoder), std::move(video_frame_callback), config));
}

void ExtractVideoFrameOnMediaThread(
    media::DataSource* data_source,
    MediaParser::ExtractVideoFrameCallback video_frame_callback) {
  auto extractor = std::make_unique<media::VideoFrameExtractor>(data_source);
  auto* const extractor_ptr = extractor.get();
  extractor_ptr->Start(base::BindOnce(&OnEncodedVideoFrameExtracted,
                                      std::move(extractor),
                                      std::move(video_frame_callback)));
}

}  // namespace

VideoThumbnailParser::VideoThumbnailParser(
    std::unique_ptr<media::DataSource> source)
    : data_source_(std::move(source)),
      media_task_runner_(
          base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) {}

VideoThumbnailParser::~VideoThumbnailParser() = default;

void VideoThumbnailParser::Start(
    MediaParser::ExtractVideoFrameCallback video_frame_callback) {
  media_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &ExtractVideoFrameOnMediaThread, data_source_.get(),
          base::BindPostTaskToCurrentDefault(std::move(video_frame_callback))));
}