blob: a7b1c9d0f1031bb24a534be72f8af6e4bd766bea [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. 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_CAPTURE_MEDIA_RECORDER_H_
#define COBALT_MEDIA_CAPTURE_MEDIA_RECORDER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "cobalt/dom/blob_property_bag.h"
#include "cobalt/dom/event_target.h"
#include "cobalt/media_capture/encoders/audio_encoder.h"
#include "cobalt/media_capture/media_recorder_options.h"
#include "cobalt/media_capture/recording_state.h"
#include "cobalt/media_stream/audio_parameters.h"
#include "cobalt/media_stream/media_stream.h"
#include "cobalt/media_stream/media_stream_audio_sink.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/wrappable.h"
namespace cobalt {
namespace media_capture {
// This class represents a MediaRecorder, and implements the specification at:
// https://www.w3.org/TR/mediastream-recording/#mediarecorder-api
class MediaRecorder : public media_stream::MediaStreamAudioSink,
public encoders::AudioEncoder::Listener,
public dom::EventTarget {
public:
// Constructors.
explicit MediaRecorder(script::EnvironmentSettings* settings,
const scoped_refptr<media_stream::MediaStream>& stream,
script::ExceptionState* exception_state);
explicit MediaRecorder(script::EnvironmentSettings* settings,
const scoped_refptr<media_stream::MediaStream>& stream,
const MediaRecorderOptions& options,
script::ExceptionState* exception_state);
~MediaRecorder() override;
// Readonly attributes.
const std::string& mime_type() const { return mime_type_; }
// Functions
static bool IsTypeSupported(const base::StringPiece mime_type);
void Start(int32 timeslice, script::ExceptionState* exception_state);
void Start(script::ExceptionState* exception_state) {
timeslice_unspecified_ = true;
Start(kint32max, exception_state);
}
void Stop(script::ExceptionState* exception_state);
// EventHandlers.
const EventListenerScriptValue* onstart() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GetAttributeEventListener(base::Tokens::start());
}
void set_onstart(const EventListenerScriptValue& event_listener) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
SetAttributeEventListener(base::Tokens::start(), event_listener);
}
const EventListenerScriptValue* onstop() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GetAttributeEventListener(base::Tokens::stop());
}
void set_onstop(const EventListenerScriptValue& event_listener) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
SetAttributeEventListener(base::Tokens::stop(), event_listener);
}
const EventListenerScriptValue* onerror() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GetAttributeEventListener(base::Tokens::error());
}
void set_onerror(const EventListenerScriptValue& event_listener) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
SetAttributeEventListener(base::Tokens::error(), event_listener);
}
const EventListenerScriptValue* ondataavailable() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GetAttributeEventListener(base::Tokens::dataavailable());
}
void set_ondataavailable(const EventListenerScriptValue& event_listener) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
SetAttributeEventListener(base::Tokens::dataavailable(), event_listener);
}
RecordingState state() { return recording_state_; }
// MediaStreamAudioSink overrides.
void OnData(const ShellAudioBus& audio_bus,
base::TimeTicks reference_time) override;
void OnSetFormat(const media_stream::AudioParameters& params) override;
void OnReadyStateChanged(
media_stream::MediaStreamTrack::ReadyState ready_state) override;
DEFINE_WRAPPABLE_TYPE(MediaRecorder);
private:
MediaRecorder(const MediaRecorder&) = delete;
MediaRecorder& operator=(const MediaRecorder&) = delete;
void StopRecording();
void UnsubscribeFromTrack();
void DoOnDataCallback(base::TimeTicks timecode);
void OnEncodedDataAvailable(const uint8* data, size_t data_size,
base::TimeTicks timecode) override;
void WriteData(std::unique_ptr<std::vector<uint8>> data, bool last_in_slice,
base::TimeTicks timecode);
void CalculateLastInSliceAndWriteData(
std::unique_ptr<std::vector<uint8>> data, base::TimeTicks timecode);
THREAD_CHECKER(thread_checker_);
// |audio_encoder_| lives on the microphone thread.
std::unique_ptr<encoders::AudioEncoder> audio_encoder_;
std::vector<uint8> buffer_;
RecordingState recording_state_ = kRecordingStateInactive;
script::EnvironmentSettings* settings_;
std::string mime_type_;
dom::BlobPropertyBag blob_options_;
scoped_refptr<media_stream::MediaStream> stream_;
scoped_refptr<base::SingleThreadTaskRunner> javascript_message_loop_;
base::TimeDelta timeslice_;
// Only used to determine if |timeslice_| amount of time has
// been passed since the slice was started.
base::TimeTicks slice_origin_timestamp_;
base::WeakPtrFactory<MediaRecorder> weak_ptr_factory_;
base::WeakPtr<MediaRecorder> weak_this_;
bool timeslice_unspecified_ = false;
};
} // namespace media_capture
} // namespace cobalt
#endif // COBALT_MEDIA_CAPTURE_MEDIA_RECORDER_H_