MediaRecorderPrivate.cpp [plain text]
#include "config.h"
#include "MediaRecorderPrivate.h"
#if PLATFORM(COCOA) && ENABLE(GPU_PROCESS) && ENABLE(MEDIA_STREAM) && HAVE(AVASSETWRITERDELEGATE)
#include "DataReference.h"
#include "GPUProcessConnection.h"
#include "RemoteMediaRecorderManagerMessages.h"
#include "RemoteMediaRecorderMessages.h"
#include "WebProcess.h"
#include <WebCore/CARingBuffer.h>
#include <WebCore/MediaStreamPrivate.h>
#include <WebCore/MediaStreamTrackPrivate.h>
#include <WebCore/RemoteVideoSample.h>
#include <WebCore/SharedBuffer.h>
#include <WebCore/WebAudioBufferList.h>
namespace WebKit {
using namespace WebCore;
MediaRecorderPrivate::MediaRecorderPrivate(MediaStreamPrivate& stream, const MediaRecorderPrivateOptions& options)
: m_identifier(MediaRecorderIdentifier::generate())
, m_stream(makeRef(stream))
, m_connection(WebProcess::singleton().ensureGPUProcessConnection().connection())
, m_options(options)
, m_hasVideo(stream.hasVideo())
{
}
void MediaRecorderPrivate::startRecording(StartRecordingCallback&& callback)
{
auto selectedTracks = MediaRecorderPrivate::selectTracks(m_stream);
if (selectedTracks.audioTrack)
m_ringBuffer = makeUnique<CARingBuffer>(makeUniqueRef<SharedRingBufferStorage>(std::bind(&MediaRecorderPrivate::storageChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
m_connection->sendWithAsyncReply(Messages::RemoteMediaRecorderManager::CreateRecorder { m_identifier, !!selectedTracks.audioTrack, !!selectedTracks.videoTrack, m_options }, [this, weakThis = makeWeakPtr(this), audioTrack = makeRefPtr(selectedTracks.audioTrack), videoTrack = makeRefPtr(selectedTracks.videoTrack), callback = WTFMove(callback)](auto&& exception, String&& mimeType, unsigned audioBitRate, unsigned videoBitRate) mutable {
if (!weakThis) {
callback(Exception { InvalidStateError }, 0, 0);
return;
}
if (exception) {
callback(Exception { exception->code, WTFMove(exception->message) }, 0, 0);
return;
}
if (!m_isStopped) {
if (audioTrack)
setAudioSource(&audioTrack->source());
if (videoTrack)
setVideoSource(&videoTrack->source());
}
callback(WTFMove(mimeType), audioBitRate, videoBitRate);
}, 0);
}
MediaRecorderPrivate::~MediaRecorderPrivate()
{
m_connection->send(Messages::RemoteMediaRecorderManager::ReleaseRecorder { m_identifier }, 0);
}
void MediaRecorderPrivate::videoSampleAvailable(MediaSample& sample)
{
if (auto remoteSample = RemoteVideoSample::create(sample))
m_connection->send(Messages::RemoteMediaRecorder::VideoSampleAvailable { WTFMove(*remoteSample) }, m_identifier);
}
void MediaRecorderPrivate::audioSamplesAvailable(const MediaTime& time, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t numberOfFrames)
{
if (m_description != description) {
ASSERT(description.platformDescription().type == PlatformDescription::CAAudioStreamBasicType);
m_description = *WTF::get<const AudioStreamBasicDescription*>(description.platformDescription().description);
m_numberOfFrames = m_description.sampleRate() * 2;
m_ringBuffer->allocate(m_description.streamDescription(), m_numberOfFrames);
}
ASSERT(is<WebAudioBufferList>(audioData));
m_ringBuffer->store(downcast<WebAudioBufferList>(audioData).list(), numberOfFrames, time.timeValue());
m_connection->send(Messages::RemoteMediaRecorder::AudioSamplesAvailable { time, numberOfFrames }, m_identifier);
}
void MediaRecorderPrivate::storageChanged(SharedMemory* storage, const WebCore::CAAudioStreamDescription& format, size_t frameCount)
{
SharedMemory::Handle handle;
if (storage)
storage->createHandle(handle, SharedMemory::Protection::ReadOnly);
#if OS(DARWIN) || OS(WINDOWS)
uint64_t dataSize = handle.size();
#else
uint64_t dataSize = 0;
#endif
m_connection->send(Messages::RemoteMediaRecorder::AudioSamplesStorageChanged { SharedMemory::IPCHandle { WTFMove(handle), dataSize }, format, frameCount }, m_identifier);
}
void MediaRecorderPrivate::fetchData(CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&, const String& mimeType, double)>&& completionHandler)
{
m_connection->sendWithAsyncReply(Messages::RemoteMediaRecorder::FetchData { }, [completionHandler = WTFMove(completionHandler), mimeType = mimeType()](auto&& data, double timeCode) mutable {
RefPtr<SharedBuffer> buffer;
if (data.size())
buffer = SharedBuffer::create(data.data(), data.size());
completionHandler(WTFMove(buffer), mimeType, timeCode);
}, m_identifier);
}
void MediaRecorderPrivate::stopRecording()
{
m_isStopped = true;
m_connection->send(Messages::RemoteMediaRecorder::StopRecording { }, m_identifier);
}
void MediaRecorderPrivate::pauseRecording(CompletionHandler<void()>&& completionHandler)
{
m_connection->sendWithAsyncReply(Messages::RemoteMediaRecorder::Pause { }, WTFMove(completionHandler), m_identifier);
}
void MediaRecorderPrivate::resumeRecording(CompletionHandler<void()>&& completionHandler)
{
m_connection->sendWithAsyncReply(Messages::RemoteMediaRecorder::Resume { }, WTFMove(completionHandler), m_identifier);
}
const String& MediaRecorderPrivate::mimeType() const
{
static NeverDestroyed<const String> audioMP4(MAKE_STATIC_STRING_IMPL("audio/mp4"));
static NeverDestroyed<const String> videoMP4(MAKE_STATIC_STRING_IMPL("video/mp4"));
return m_hasVideo ? videoMP4 : audioMP4;
}
}
#endif // PLATFORM(COCOA) && ENABLE(GPU_PROCESS) && ENABLE(MEDIA_STREAM) && HAVE(AVASSETWRITERDELEGATE)