blob: 201874d6c5b831f118d0b7fdaa4ff4a69aa3f32c [file] [log] [blame]
// Copyright 2018 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/browser/android/download/local_media_data_source_factory.h"
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop_current.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace {
using MediaDataCallback =
SafeMediaMetadataParser::MediaDataSourceFactory::MediaDataCallback;
using ReadFileCallback = base::OnceCallback<void(bool, std::vector<char>)>;
// Reads a chunk of the file on a file thread, and reply the data or error to
// main thread.
void ReadFile(const base::FilePath& file_path,
int64_t position,
int64_t length,
scoped_refptr<base::SequencedTaskRunner> main_task_runner,
ReadFileCallback cb) {
base::File file(file_path,
base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
if (!file.IsValid()) {
main_task_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), false /*success*/, std::vector<char>()));
return;
}
auto buffer = std::vector<char>(length);
int bytes_read = file.Read(position, buffer.data(), length);
if (bytes_read == -1) {
main_task_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), false /*success*/, std::vector<char>()));
return;
}
DCHECK_GE(bytes_read, 0);
if (bytes_read < length)
buffer.resize(bytes_read);
main_task_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), true /*success*/, std::move(buffer)));
}
// Read media file incrementally and send data to the utility process to parse
// media metadata. Must live and die on main thread and does blocking IO on
// |file_task_runner_|.
class LocalMediaDataSource : public chrome::mojom::MediaDataSource {
public:
LocalMediaDataSource(
chrome::mojom::MediaDataSourcePtr* interface,
const base::FilePath& file_path,
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
MediaDataCallback media_data_callback)
: file_path_(file_path),
file_task_runner_(file_task_runner),
media_data_callback_(media_data_callback),
binding_(this, mojo::MakeRequest(interface)),
weak_ptr_factory_(this) {}
~LocalMediaDataSource() override = default;
private:
// chrome::mojom::MediaDataSource implementation.
void Read(int64_t position,
int64_t length,
chrome::mojom::MediaDataSource::ReadCallback callback) override {
DCHECK(!ipc_read_callback_);
ipc_read_callback_ = std::move(callback);
// Read file on a file thread.
ReadFileCallback read_file_done = base::BindOnce(
&LocalMediaDataSource::OnReadFileDone, weak_ptr_factory_.GetWeakPtr());
file_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ReadFile, file_path_, position, length,
base::MessageLoopCurrent::Get()->task_runner(),
std::move(read_file_done)));
}
void OnReadFileDone(bool success, std::vector<char> buffer) {
// TODO(xingliu): Handle file IO error when success is false, the IPC
// channel for chrome::mojom::MediaParser should be closed.
DCHECK(ipc_read_callback_);
media_data_callback_.Run(
std::move(ipc_read_callback_),
std::make_unique<std::string>(buffer.begin(), buffer.end()));
}
base::FilePath file_path_;
scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
// Called when a chunk of the file is read.
MediaDataCallback media_data_callback_;
// Pass through callback that is used to send data across IPC channel.
chrome::mojom::MediaDataSource::ReadCallback ipc_read_callback_;
mojo::Binding<chrome::mojom::MediaDataSource> binding_;
base::WeakPtrFactory<LocalMediaDataSource> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(LocalMediaDataSource);
};
} // namespace
LocalMediaDataSourceFactory::LocalMediaDataSourceFactory(
const base::FilePath& file_path,
scoped_refptr<base::SequencedTaskRunner> file_task_runner)
: file_path_(file_path), file_task_runner_(file_task_runner) {}
LocalMediaDataSourceFactory::~LocalMediaDataSourceFactory() = default;
std::unique_ptr<chrome::mojom::MediaDataSource>
LocalMediaDataSourceFactory::CreateMediaDataSource(
chrome::mojom::MediaDataSourcePtr* request,
MediaDataCallback media_data_callback) {
return std::make_unique<LocalMediaDataSource>(
request, file_path_, file_task_runner_, media_data_callback);
}