blob: 29e7ccee46829c69c8c53e8def73d0069a9f40c5 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef COBALT_MEDIA_FETCHER_BUFFERED_DATA_SOURCE_H_
#define COBALT_MEDIA_FETCHER_BUFFERED_DATA_SOURCE_H_
#include <string>
#include "base/callback.h"
#include "base/circular_buffer_shell.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop_proxy.h"
#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "cobalt/csp/content_security_policy.h"
#include "cobalt/network/network_module.h"
#include "googleurl/src/gurl.h"
#if defined(COBALT_MEDIA_SOURCE_2016)
#include "cobalt/media/player/buffered_data_source.h"
#else // defined(COBALT_MEDIA_SOURCE_2016)
#include "media/player/buffered_data_source.h"
#endif // defined(COBALT_MEDIA_SOURCE_2016)
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
namespace cobalt {
namespace media {
#if !defined(COBALT_MEDIA_SOURCE_2016)
typedef ::media::BufferedDataSource BufferedDataSource;
#endif // !defined(WebMediaPlayerDelegate)
// TODO: This class requires a large block of memory. Consider to
// use ShellBufferFactory for its memory if possible to avoid possible OOM.
// A BufferedDataSource based on URLFetcher that can be used to retrieve
// progressive videos from both local and network sources.
// It uses a fixed size circular buffer so we may not be able to store all data
// into this buffer. It is based on the following assumptions/strategies:
// 1. It assumes that the buffer is large enough to fulfill one Read() request.
// So any outstanding request only requires at most one request.
// 2. It will do one initial request to retrieve the target resource. If the
// whole resource can be fit into the buffer, no further request will be
// fired.
// 3. If the resource doesn't fit into the buffer. The class will store
// kBackwardBytes bytes before the last read offset(LRO) and kForwardBytes
// after LRO. Note that if LRO is less than kBackwardBytes, then data starts
// from offset 0 will be cached.
// 4. It assumes that the server supports range request.
// 5. All data stored are continuous.
class FetcherBufferedDataSource : public BufferedDataSource,
private net::URLFetcherDelegate {
public:
static const int64 kInvalidSize = -1;
// Because the Fetchers have to be created and destroyed on the same thread,
// we use the message_loop passed in to create and destroy Fetchers.
FetcherBufferedDataSource(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
const GURL& url, const csp::SecurityCallback& security_callback,
network::NetworkModule* network_module);
~FetcherBufferedDataSource() OVERRIDE;
// DataSource methods.
void Read(int64 position, int size, uint8* data,
const ReadCB& read_cb) OVERRIDE;
void Stop() OVERRIDE;
bool GetSize(int64* size_out) OVERRIDE;
bool IsStreaming() OVERRIDE { return false; }
void SetBitrate(int bitrate) OVERRIDE { UNREFERENCED_PARAMETER(bitrate); }
// BufferedDataSource methods.
void SetDownloadingStatusCB(const DownloadingStatusCB& downloading_status_cb);
private:
class CancelableClosure
: public base::RefCountedThreadSafe<CancelableClosure> {
public:
explicit CancelableClosure(const base::Closure& closure);
void Cancel();
base::Closure AsClosure();
private:
void Call();
base::Lock lock_;
base::Closure closure_;
};
// net::URLFetcherDelegate methods
void OnURLFetchResponseStarted(const net::URLFetcher* source) OVERRIDE;
bool ShouldSendDownloadData() OVERRIDE { return true; }
void OnURLFetchDownloadData(const net::URLFetcher* source,
scoped_ptr<std::string> download_data) OVERRIDE;
void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
void CreateNewFetcher();
void UpdateDownloadingStatus(bool is_downloading);
void Read_Locked(uint64 position, size_t size, uint8* data,
const ReadCB& read_cb);
void ProcessPendingRead_Locked();
void TryToSendRequest_Locked();
base::Lock lock_;
scoped_refptr<base::MessageLoopProxy> message_loop_;
GURL url_;
network::NetworkModule* network_module_;
scoped_ptr<net::URLFetcher> fetcher_;
bool is_downloading_;
DownloadingStatusCB downloading_status_cb_;
// |fetcher_| has to be destroyed on the thread it's created. So it cannot be
// safely destroyed inside Read_Locked(). Save |fetcher_| into
// |fetcher_to_be_destroyed_| to ensure that it is properly destroyed either
// inside CreateNewFetcher() or in the dtor while still allow |fetcher_| to be
// set to NULL to invalidate outstanding read.
scoped_ptr<net::URLFetcher> fetcher_to_be_destroyed_;
// |buffer_| stores a continuous block of data of target resource starts from
// |buffer_offset_|. When the target resource can be fit into |buffer_|,
// |buffer_offset_| will always be 0.
base::CircularBufferShell buffer_;
uint64 buffer_offset_;
base::optional<uint64> total_size_of_resource_;
bool error_occured_;
uint64 last_request_offset_;
uint64 last_request_size_;
// This is usually the same as pending_read_position_. Represent it
// explicitly using a separate variable.
uint64 last_read_position_;
ReadCB pending_read_cb_;
uint64 pending_read_position_;
size_t pending_read_size_;
uint8* pending_read_data_;
csp::SecurityCallback security_callback_;
scoped_refptr<CancelableClosure> cancelable_create_fetcher_closure_;
};
} // namespace media
} // namespace cobalt
#endif // COBALT_MEDIA_FETCHER_BUFFERED_DATA_SOURCE_H_