5#include <qaudiodevice.h>
6#include <qcameradevice.h>
11JsMediaRecorder::JsMediaRecorder() =
default;
15 if (mode.testFlag(QIODevice::WriteOnly))
17 return QIODevice::open(mode);
27 return m_buffer.size();
34 return QIODevice::seek(pos);
39 qint64 bytesToRead = qMin(maxSize, (qint64)m_buffer.size());
40 memcpy(data, m_buffer.constData(), bytesToRead);
41 m_buffer = m_buffer.right(m_buffer.size() - bytesToRead);
47 Q_UNREACHABLE_RETURN(0);
50void JsMediaRecorder::audioDataAvailable(emscripten::val aBlob,
double timeCodeDifference)
52 Q_UNUSED(timeCodeDifference)
53 if (aBlob.isUndefined() || aBlob.isNull()) {
54 qWarning() <<
"blob is null";
58 auto fileReader = std::make_shared<qstdweb::FileReader>();
60 fileReader->onError([=](emscripten::val theError) {
61 emit streamError(QMediaRecorder::ResourceError,
62 QString::fromStdString(theError[
"message"].as<std::string>()));
65 fileReader->onAbort([=](emscripten::val) {
66 emit streamError(QMediaRecorder::ResourceError, QStringLiteral(
"File read aborted"));
69 fileReader->onLoad([=](emscripten::val) {
70 if (fileReader->val().isNull() || fileReader->val().isUndefined())
72 qstdweb::ArrayBuffer result = fileReader->result();
73 if (result.val().isNull() || result.val().isUndefined())
76 m_buffer.append(qstdweb::Uint8Array(result).copyToQByteArray());
80 fileReader->readAsArrayBuffer(qstdweb::Blob(aBlob));
83void JsMediaRecorder::setTrackContraints(QMediaEncoderSettings &settings, emscripten::val stream)
85 if (stream.isUndefined() || stream.isNull()) {
86 qWarning()<<
"could not find MediaStream";
90 emscripten::val navigator = emscripten::val::global(
"navigator");
91 emscripten::val mediaDevices = navigator[
"mediaDevices"];
94 emscripten::val allConstraints = mediaDevices.call<emscripten::val>(
"getSupportedConstraints");
97 emscripten::val videoParams = emscripten::val::object();
98 emscripten::val constraints = emscripten::val::object();
99 videoParams.set(
"resizeMode",std::string(
"crop-and-scale"));
102 if (settings.videoFrameRate() > 0)
103 videoParams.set(
"frameRate", emscripten::val(settings.videoFrameRate()));
104 if (settings.videoResolution().height() > 0)
105 videoParams.set(
"height",
106 emscripten::val(settings.videoResolution().height()));
107 if (settings.videoResolution().width() > 0)
108 videoParams.set(
"width", emscripten::val(settings.videoResolution().width()));
110 constraints.set(
"video", videoParams);
113 emscripten::val audioParams = emscripten::val::object();
114 if (settings.audioSampleRate() > 0)
115 audioParams.set(
"sampleRate", emscripten::val(settings.audioSampleRate()));
116 if (settings.audioBitRate() > 0)
117 audioParams.set(
"sampleSize", emscripten::val(settings.audioBitRate()));
118 if (settings.audioChannelCount() > 0)
119 audioParams.set(
"channelCount", emscripten::val(settings.audioChannelCount()));
121 constraints.set(
"audio", audioParams);
123 if (m_needsCamera && stream[
"active"].as<
bool>()) {
124 emscripten::val videoTracks = emscripten::val::undefined();
125 videoTracks = stream.call<emscripten::val>(
"getVideoTracks");
126 if (videoTracks.isNull() || videoTracks.isUndefined()) {
127 qWarning() <<
"no video tracks";
130 if (videoTracks[
"length"].as<
int>() > 0) {
132 qstdweb::Promise::make(videoTracks[0],
133 QStringLiteral(
"applyConstraints"), {
135 [
this]([[maybe_unused]] emscripten::val result) {
139 [
this](emscripten::val theError) {
141 << theError[
"code"].as<
int>()
142 << theError[
"message"].as<std::string>();
143 emit streamError(QMediaRecorder::ResourceError,
144 QString::fromStdString(theError[
"message"].as<std::string>()));
146 .finallyFunc = []() {},
155 if (m_mediaRecorder.isUndefined() || m_mediaRecorder.isNull()) {
156 qWarning() <<
"could not find MediaRecorder";
159 m_mediaRecorder.call<
void>(
"pause");
164 if (m_mediaRecorder.isUndefined() || m_mediaRecorder.isNull()) {
165 qWarning() <<
"could not find MediaRecorder";
169 m_mediaRecorder.call<
void>(
"resume");
174 if (m_mediaRecorder.isUndefined() || m_mediaRecorder.isNull()) {
175 qWarning()<<
"could not find MediaRecorder";
178 if (m_mediaRecorder[
"state"].as<std::string>() ==
"recording")
179 m_mediaRecorder.call<
void>(
"stop");
185 if (m_mediaRecorder.isUndefined() || m_mediaRecorder.isNull()) {
186 qWarning() <<
"could not find MediaStream";
190 constexpr int sliceSizeInMs = 256;
192 m_mediaRecorder.call<
void>(
"start", emscripten::val(sliceSizeInMs));
197 emscripten::val emMediaSettings = emscripten::val::object();
198 QMediaFormat::VideoCodec videoCodec = m_mediaSettings.videoCodec();
199 QMediaFormat::AudioCodec audioCodec = m_mediaSettings.audioCodec();
200 QMediaFormat::FileFormat fileFormat = m_mediaSettings.fileFormat();
204 if (!m_mediaSettings.mimeType().name().isEmpty()) {
205 mimeCodec = m_mediaSettings.mimeType().name();
207 if (videoCodec != QMediaFormat::VideoCodec::Unspecified)
208 mimeCodec += QStringLiteral(
": codecs=");
210 if (audioCodec != QMediaFormat::AudioCodec::Unspecified) {
214 if (fileFormat != QMediaFormat::UnspecifiedFormat)
215 mimeCodec += QMediaFormat::fileFormatName(m_mediaSettings.fileFormat());
217 emMediaSettings.set(
"mimeType", mimeCodec.toStdString());
220 if (m_mediaSettings.audioBitRate() > 0)
221 emMediaSettings.set(
"audioBitsPerSecond", emscripten::val(m_mediaSettings.audioBitRate()));
223 if (m_mediaSettings.videoBitRate() > 0)
224 emMediaSettings.set(
"videoBitsPerSecond", emscripten::val(m_mediaSettings.videoBitRate()));
227 m_mediaRecorder = emscripten::val::global(
"MediaRecorder").new_(stream, emMediaSettings);
229 if (m_mediaRecorder.isNull() || m_mediaRecorder.isUndefined()) {
230 qWarning() <<
"MediaRecorder could not be found";
233 m_mediaRecorder.set(
"data-mediarecordercontext",
234 emscripten::val(quintptr(
reinterpret_cast<
void *>(
this))));
236 if (!m_mediaStreamDataAvailable.isNull()) {
237 m_mediaStreamDataAvailable.reset();
238 m_mediaStreamStopped.reset();
239 m_mediaStreamError.reset();
240 m_mediaStreamStart.reset();
241 m_mediaStreamPause.reset();
242 m_mediaStreamResume.reset();
246 auto callback = [](emscripten::val blob) {
247 if (blob.isUndefined() || blob.isNull()) {
248 qWarning() <<
"blob is null";
251 if (blob[
"target"].isUndefined() || blob[
"target"].isNull())
253 if (blob[
"data"].isUndefined() || blob[
"data"].isNull())
255 if (blob[
"target"][
"data-mediarecordercontext"].isUndefined()
256 || blob[
"target"][
"data-mediarecordercontext"].isNull())
260 blob[
"target"][
"data-mediarecordercontext"].as<quintptr>());
263 const double timeCode =
264 blob.hasOwnProperty(
"timecode") ? blob[
"timecode"].as<
double>() : 0;
265 recorder->audioDataAvailable(blob[
"data"], timeCode);
269 m_mediaStreamDataAvailable.reset(
270 new qstdweb::EventCallback(m_mediaRecorder,
"dataavailable", callback));
273 auto stoppedCallback = [
this](emscripten::val event) {
274 if (event.isUndefined() || event.isNull()) {
275 qWarning() <<
"event is null";
278 m_currentState = QMediaRecorder::StoppedState;
280 event[
"target"][
"data-mediarecordercontext"].as<quintptr>());
281 emit recorder->stopped();
284 m_mediaStreamStopped.reset(
285 new qstdweb::EventCallback(m_mediaRecorder,
"stop", stoppedCallback));
288 auto errorCallback = [
this](emscripten::val theError) {
289 if (theError.isUndefined() || theError.isNull()) {
290 qWarning() <<
"error is null";
294 emit streamError(QMediaRecorder::ResourceError,
295 QString::fromStdString(theError[
"message"].as<std::string>()));
298 m_mediaStreamError.reset(
new qstdweb::EventCallback(m_mediaRecorder,
"error", errorCallback));
301 auto startCallback = [
this](emscripten::val event) {
302 if (event.isUndefined() || event.isNull()) {
303 qWarning() <<
"event is null";
308 event[
"target"][
"data-mediarecordercontext"].as<quintptr>());
309 m_currentState = QMediaRecorder::RecordingState;
310 emit recorder->started();
313 m_mediaStreamStart.reset(
new qstdweb::EventCallback(m_mediaRecorder,
"start", startCallback));
316 auto pauseCallback = [
this](emscripten::val event) {
317 if (event.isUndefined() || event.isNull()) {
318 qWarning() <<
"event is null";
323 event[
"target"][
"data-mediarecordercontext"].as<quintptr>());
324 m_currentState = QMediaRecorder::PausedState;
325 emit recorder->paused();
328 m_mediaStreamPause.reset(
new qstdweb::EventCallback(m_mediaRecorder,
"pause", pauseCallback));
331 auto resumeCallback = [
this](emscripten::val event) {
332 if (event.isUndefined() || event.isNull()) {
333 qWarning() <<
"event is null";
336 m_currentState = QMediaRecorder::RecordingState;
339 event[
"target"][
"data-mediarecordercontext"].as<quintptr>());
340 emit recorder->resumed();
343 m_mediaStreamResume.reset(
344 new qstdweb::EventCallback(m_mediaRecorder,
"resume", resumeCallback));
349 return m_buffer.size();
360 emscripten::val navigator = emscripten::val::global(
"navigator");
361 emscripten::val mediaDevices = navigator[
"mediaDevices"];
363 if (mediaDevices.isNull() || mediaDevices.isUndefined()) {
364 qWarning() <<
"No media devices found";
368 qstdweb::PromiseCallbacks getUserMediaCallback{
371 [
this](emscripten::val stream) {
372 m_mediaStream = std::move(stream);
373 emit mediaStreamReady();
376 [](emscripten::val error) {
378 <<
"setStreamDevice getUserMedia fail"
379 << error[
"name"].as<std::string>()
380 << error[
"message"].as<std::string>();
384 emscripten::val constraints = emscripten::val::object();
386 emscripten::val audioConstraints = emscripten::val::object();
387 audioConstraints.set(
"audio", m_needsVideo);
388 emscripten::val exactDeviceId = emscripten::val::object();
389 exactDeviceId.set(
"exact", id);
390 audioConstraints.set(
"deviceId", exactDeviceId);
391 constraints.set(
"audio", audioConstraints);
395 emscripten::val videoContraints = emscripten::val::object();
396 emscripten::val exactDeviceId = emscripten::val::object();
397 exactDeviceId.set(
"exact", id);
398 videoContraints.set(
"deviceId", exactDeviceId);
399 videoContraints.set(
"resizeMode", std::string(
"crop-and-scale"));
400 constraints.set(
"video", videoContraints);
411 qstdweb::Promise::make(mediaDevices, QStringLiteral(
"getUserMedia"),
412 std::move(getUserMediaCallback), constraints);
417 m_mediaStream = mStream;
419 auto activeStreamCallback = [=](emscripten::val event) {
421 emit activated(m_active);
423 m_activeStreamEvent.reset(
new qstdweb::EventCallback(m_mediaStream,
"active", activeStreamCallback));
425 auto inactiveStreamCallback = [=](emscripten::val event) {
427 emit activated(m_active);
429 m_inactiveStreamEvent.reset(
new qstdweb::EventCallback(m_mediaStream,
"inactive", inactiveStreamCallback));