blob: 8fab6e0ec8c59fa29af3e7cb75aa06aeeb40e137 [file] [log] [blame]
// Copyright 2019 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 "chrome/services/media_gallery_util/video_thumbnail_parser.h"
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/task/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/bind_to_current_loop.h"
#include "media/base/video_codecs.h"
#include "media/base/video_thumbnail_decoder.h"
#include "media/filters/android/video_frame_extractor.h"
#include "media/filters/vpx_video_decoder.h"
#include "media/media_buildflags.h"
#include "media/mojo/common/mojo_shared_buffer_video_frame.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace {
// Return the video frame back to browser process. A valid |config| is
// needed for deserialization.
void OnSoftwareVideoFrameDecoded(
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(false, chrome::mojom::VideoFrameData::New(), absl::nullopt);
return;
}
std::move(video_frame_callback)
.Run(true,
chrome::mojom::VideoFrameData::NewDecodedFrame(
media::MojoSharedBufferVideoFrame::CreateFromYUVFrame(*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(false, chrome::mojom::VideoFrameData::New(), absl::nullopt);
return;
}
#if BUILDFLAG(USE_PROPRIETARY_CODECS) && \
!BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
// H264 currently needs to be decoded in GPU process when no software decoder
// is provided.
if (config.codec() == media::VideoCodec::kCodecH264) {
std::move(video_frame_callback)
.Run(success,
chrome::mojom::VideoFrameData::NewEncodedData(std::move(data)),
config);
return;
}
#endif
if (config.codec() != media::VideoCodec::kCodecVP8 &&
config.codec() != media::VideoCodec::kCodecVP9) {
std::move(video_frame_callback)
.Run(false, chrome::mojom::VideoFrameData::New(), absl::nullopt);
return;
}
// Decode with libvpx for vp8, vp9.
auto thumbnail_decoder = std::make_unique<media::VideoThumbnailDecoder>(
std::make_unique<media::VpxVideoDecoder>(), config, std::move(data));
auto* const thumbnail_decoder_ptr = thumbnail_decoder.get();
thumbnail_decoder_ptr->Start(
base::BindOnce(&OnSoftwareVideoFrameDecoded, 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(),
media::BindToCurrentLoop(std::move(video_frame_callback))));
}