6#include "common/qohosvideooutput_p.h"
9#include <QtCore/qdir.h>
10#include <QtCore/qfile.h>
11#include <QtCore/qfileinfo.h>
12#include <QtCore/qstandardpaths.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qmutex.h>
15#include <QtCore/qset.h>
16#include <QtCore/qthread.h>
17#include <QtCore/qthreadpool.h>
18#include <QtGui/qimage.h>
19#include <QtMultimedia/qvideosink.h>
24#include <private/qcameradevice_p.h>
25#include <private/qmediastoragelocation_p.h>
26#include <private/qmemoryvideobuffer_p.h>
27#include <private/qvideoframe_p.h>
29#include <multimedia/image_framework/image/image_native.h>
30#include <multimedia/image_framework/image/image_packer_native.h>
31#include <multimedia/image_framework/image/image_source_native.h>
32#include <native_buffer/native_buffer.h>
33#include <native_window/external_window.h>
53 static QRecursiveMutex m;
58 static QSet<QOhosCameraSession *> s;
65 case CAMERA_FORMAT_RGBA_8888:
66 return QVideoFrameFormat::Format_RGBA8888;
67 case CAMERA_FORMAT_YUV_420_SP:
68 return QVideoFrameFormat::Format_NV12;
69 case CAMERA_FORMAT_JPEG:
70 return QVideoFrameFormat::Format_Jpeg;
74 return QVideoFrameFormat::Format_Invalid;
80 case QImageCapture::VeryLowQuality:
82 case QImageCapture::LowQuality:
84 case QImageCapture::HighQuality:
86 case QImageCapture::VeryHighQuality:
88 case QImageCapture::NormalQuality:
96 uint32_t *components =
nullptr;
97 size_t componentCount = 0;
98 if (OH_ImageNative_GetComponentTypes(image,
nullptr, &componentCount) != IMAGE_SUCCESS
99 || componentCount == 0) {
102 std::vector<uint32_t> types(componentCount);
103 components = types.data();
104 if (OH_ImageNative_GetComponentTypes(image, &components, &componentCount) != IMAGE_SUCCESS)
107 OH_NativeBuffer *nativeBuffer =
nullptr;
108 if (OH_ImageNative_GetByteBuffer(image, types[0], &nativeBuffer) != IMAGE_SUCCESS
113 OH_NativeBuffer_Config bufferConfig{};
114 OH_NativeBuffer_GetConfig(nativeBuffer, &bufferConfig);
116 void *mapped =
nullptr;
117 if (OH_NativeBuffer_Map(nativeBuffer, &mapped) != 0 || !mapped)
120 size_t bufferSize = 0;
121 OH_ImageNative_GetBufferSize(image, types[0], &bufferSize);
124 OH_ImageSourceNative *source =
nullptr;
125 if (OH_ImageSourceNative_CreateFromData(
static_cast<uint8_t *>(mapped), bufferSize, &source)
128 OH_ImagePackerNative *packer =
nullptr;
129 if (OH_ImagePackerNative_Create(&packer) == IMAGE_SUCCESS && packer) {
130 OH_PackingOptions *options =
nullptr;
131 if (OH_PackingOptions_Create(&options) == IMAGE_SUCCESS && options) {
132 Image_MimeType mime{
const_cast<
char *>(kJpegMimeType),
133 std::strlen(kJpegMimeType) };
134 OH_PackingOptions_SetMimeType(options, &mime);
135 OH_PackingOptions_SetQuality(options, uint32_t(quality));
137 size_t outSize = bufferSize * 2 + 1024;
138 result.resize(
int(outSize));
139 if (OH_ImagePackerNative_PackToDataFromImageSource(
140 packer, options, source,
141 reinterpret_cast<uint8_t *>(result.data()), &outSize)
143 result.resize(
int(outSize));
147 OH_PackingOptions_Release(options);
149 OH_ImagePackerNative_Release(packer);
151 OH_ImageSourceNative_Release(source);
154 OH_NativeBuffer_Unmap(nativeBuffer);
163 QMetaObject::invokeMethod(session, &QOhosCameraSession::onCapturedImageAvailable,
164 Qt::QueuedConnection);
174 QMutexLocker lock{ &liveSessionMutex() };
178 if (m_supportedDevices) {
179 OH_CameraManager_DeleteSupportedCameras(m_manager, m_supportedDevices,
180 m_supportedDeviceCount);
181 m_supportedDevices =
nullptr;
184 OH_Camera_DeleteCameraManager(m_manager);
191 if (m_cameraDevice == camera)
193 const bool wasActive = m_active;
196 m_cameraDevice = camera;
203 m_cameraFormat = format;
208 if (m_videoSink == sink)
212 m_videoOutput.reset();
213 m_videoOutput = std::make_unique<QOhosVideoOutput>(
214 sink, QOhosVideoOutput::ContentSource::Camera,
this);
215 connect(m_videoOutput.get(), &QOhosVideoOutput::surfaceReady,
this,
216 &QOhosCameraSession::onSurfaceReady);
221 if (m_active == active)
224 if (!startSession()) {
225 m_pendingStart =
true;
229 m_pendingStart =
false;
233 emit activeChanged(active);
234 emitReadyForCaptureChanged();
239 m_imageSettings = settings;
244 const int id = ++m_lastCaptureId;
245 if (!m_active || !m_photoOutput) {
246 emit imageCaptureError(id, QImageCapture::NotReadyError,
247 tr(
"Camera not ready for capture"));
250 if (m_captureInProgress) {
251 emit imageCaptureError(id, QImageCapture::NotReadyError,
252 tr(
"Capture already in progress"));
256 m_pendingCaptureId = id;
257 m_pendingCaptureFileName = fileName;
258 m_pendingCaptureToBuffer = toBuffer;
259 m_captureInProgress =
true;
260 emitReadyForCaptureChanged();
262 if (OH_PhotoOutput_Capture(m_photoOutput) != CAMERA_OK) {
263 m_captureInProgress =
false;
264 emit imageCaptureError(id, QImageCapture::ResourceError,
265 tr(
"OH_PhotoOutput_Capture failed"));
266 emitReadyForCaptureChanged();
273 if (m_pendingStart && !m_active) {
274 m_pendingStart =
false;
275 if (startSession()) {
277 emit activeChanged(
true);
278 emitReadyForCaptureChanged();
285 if (m_active && !m_previewOutput && m_videoOutput
286 && !m_videoOutput->surfaceId().isEmpty()) {
289 if (startSession()) {
291 emit activeChanged(
true);
292 emitReadyForCaptureChanged();
299 if (!m_imageReceiver)
302 OH_ImageNative *image =
nullptr;
303 if (OH_ImageReceiverNative_ReadLatestImage(m_imageReceiver, &image) != IMAGE_SUCCESS
305 m_captureInProgress =
false;
306 emit imageCaptureError(m_pendingCaptureId, QImageCapture::ResourceError,
307 tr(
"Failed to read captured image"));
308 emitReadyForCaptureChanged();
312 const int quality = qualityToInt(m_imageSettings.quality());
313 QByteArray jpegBytes = encodeNativeImageToJpeg(image, quality);
314 OH_ImageNative_Release(image);
316 const int id = m_pendingCaptureId;
317 const QString fileName = m_pendingCaptureFileName;
318 const bool toBuffer = m_pendingCaptureToBuffer;
319 m_pendingCaptureFileName.clear();
320 m_pendingCaptureToBuffer =
false;
321 m_pendingCaptureId = 0;
322 m_captureInProgress =
false;
324 if (jpegBytes.isEmpty()) {
325 emit imageCaptureError(id, QImageCapture::FormatError,
326 tr(
"Failed to encode captured image"));
327 emitReadyForCaptureChanged();
331 QImage preview = QImage::fromData(jpegBytes,
"JPEG");
332 emit imageExposed(id);
333 emit imageCaptured(id, preview);
336 QVideoFrame buffer = QVideoFramePrivate::createFrame(
337 std::make_unique<QMemoryVideoBuffer>(QByteArray(jpegBytes),
338 preview.bytesPerLine()),
339 QVideoFrameFormat(preview.size(), QVideoFrameFormat::Format_Jpeg));
340 emit imageAvailable(id, buffer);
341 emitReadyForCaptureChanged();
348 const QImageCapture::FileFormat targetFormat = m_imageSettings.format();
349 const QString defaultExt = [&]() -> QString {
350 switch (targetFormat) {
351 case QImageCapture::PNG:
return QStringLiteral(
"png");
352 case QImageCapture::WebP:
return QStringLiteral(
"webp");
353 case QImageCapture::Tiff:
return QStringLiteral(
"tiff");
354 case QImageCapture::JPEG:
355 case QImageCapture::UnspecifiedFormat:
356 default:
return QStringLiteral(
"jpg");
359 const char *qImageFormat = [&]() ->
const char * {
360 switch (targetFormat) {
361 case QImageCapture::PNG:
return "PNG";
362 case QImageCapture::WebP:
return "WEBP";
363 case QImageCapture::Tiff:
return "TIFF";
364 default:
return nullptr;
367 const QString resolved = QMediaStorageLocation::generateFileName(
368 fileName, QStandardPaths::PicturesLocation, defaultExt);
371 saved = preview.save(resolved, qImageFormat, quality);
374 if (out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
375 out.write(jpegBytes);
381 emit imageSaved(id, resolved);
383 emit imageCaptureError(id, QImageCapture::ResourceError,
384 tr(
"Could not save captured image to: %1").arg(resolved));
387 emitReadyForCaptureChanged();
393 if (m_lastReadyForCapture && *m_lastReadyForCapture == ready)
395 m_lastReadyForCapture = ready;
396 emit readyForCaptureChanged(ready);
403 if (OH_Camera_GetCameraManager(&m_manager) != CAMERA_OK || !m_manager) {
404 qCWarning(qLcOhosMediaPlugin) <<
"OH_Camera_GetCameraManager failed";
407 if (OH_CameraManager_GetSupportedCameras(m_manager, &m_supportedDevices,
408 &m_supportedDeviceCount) != CAMERA_OK) {
409 qCWarning(qLcOhosMediaPlugin) <<
"GetSupportedCameras failed";
410 m_supportedDevices =
nullptr;
411 m_supportedDeviceCount = 0;
418 if (!m_supportedDevices)
420 for (uint32_t i = 0; i < m_supportedDeviceCount; ++i) {
421 if (m_supportedDevices[i].cameraId && id == m_supportedDevices[i].cameraId)
422 return &m_supportedDevices[i];
428 Camera_Profile *previewProfile)
430 if (!caps || caps->photoProfilesSize == 0)
433 Camera_Profile *photoProfile = caps->photoProfiles[0];
434 if (m_imageSettings.resolution().isValid()) {
435 const QSize wanted = m_imageSettings.resolution();
436 for (uint32_t i = 0; i < caps->photoProfilesSize; ++i) {
437 Camera_Profile *p = caps->photoProfiles[i];
440 if (
int(p->size.width) == wanted.width()
441 &&
int(p->size.height) == wanted.height()) {
446 }
else if (previewProfile) {
447 for (uint32_t i = 0; i < caps->photoProfilesSize; ++i) {
448 Camera_Profile *p = caps->photoProfiles[i];
451 if (p->size.width == previewProfile->size.width
452 && p->size.height == previewProfile->size.height) {
461 if (OH_ImageReceiverOptions_Create(&m_imageReceiverOptions) != IMAGE_SUCCESS
462 || !m_imageReceiverOptions) {
465 Image_Size size{ uint32_t(photoProfile->size.width), uint32_t(photoProfile->size.height) };
466 OH_ImageReceiverOptions_SetSize(m_imageReceiverOptions, size);
469 if (OH_ImageReceiverNative_Create(m_imageReceiverOptions, &m_imageReceiver) != IMAGE_SUCCESS
470 || !m_imageReceiver) {
474 OH_ImageReceiverNative_OnImageArrive(m_imageReceiver, imageArriveCallbackTrampoline,
this);
476 uint64_t surfaceIdNum = 0;
477 if (OH_ImageReceiverNative_GetReceivingSurfaceId(m_imageReceiver, &surfaceIdNum) != IMAGE_SUCCESS
478 || surfaceIdNum == 0) {
481 const QByteArray surfaceId = QByteArray::number(qulonglong(surfaceIdNum));
483 if (OH_CameraManager_CreatePhotoOutput(m_manager, photoProfile, surfaceId.constData(),
484 &m_photoOutput) != CAMERA_OK
494 OH_PhotoOutput_Release(m_photoOutput);
495 m_photoOutput =
nullptr;
497 if (m_imageReceiver) {
498 OH_ImageReceiverNative_OffImageArrive(m_imageReceiver, imageArriveCallbackTrampoline);
499 OH_ImageReceiverNative_Release(m_imageReceiver);
500 m_imageReceiver =
nullptr;
502 if (m_imageReceiverOptions) {
503 OH_ImageReceiverOptions_Release(m_imageReceiverOptions);
504 m_imageReceiverOptions =
nullptr;
514 QMutexLocker lock{ &liveSessionMutex() };
515 const auto sessions = liveSessions();
516 for (
auto *other : sessions) {
518 other->stopSession();
526 if (!m_videoOutput) {
527 m_videoOutput = std::make_unique<QOhosVideoOutput>(
528 nullptr, QOhosVideoOutput::ContentSource::Camera,
this);
529 connect(m_videoOutput.get(), &QOhosVideoOutput::surfaceReady,
this,
530 &QOhosCameraSession::onSurfaceReady);
532 QByteArray previewSurfaceId = m_videoOutput->surfaceId();
534 if (!ensureManager())
537 Camera_Device *device = findDevice(m_cameraDevice.id());
538 if (!device && m_supportedDeviceCount > 0)
539 device = &m_supportedDevices[0];
541 qCWarning(qLcOhosMediaPlugin) <<
"No camera device available";
545 Camera_Profile *previewProfile =
nullptr;
546 Camera_OutputCapability *caps =
nullptr;
547 if (OH_CameraManager_GetSupportedCameraOutputCapability(m_manager, device, &caps) == CAMERA_OK
548 && caps && caps->previewProfilesSize > 0) {
549 previewProfile = caps->previewProfiles[0];
550 const bool haveRequest = !m_cameraFormat.isNull();
551 const QSize requestedSize = haveRequest ? m_cameraFormat.resolution() : QSize{};
552 const QVideoFrameFormat::PixelFormat requestedPixel =
553 haveRequest ? m_cameraFormat.pixelFormat() : QVideoFrameFormat::Format_Invalid;
554 for (uint32_t i = 0; i < caps->previewProfilesSize; ++i) {
555 Camera_Profile *p = caps->previewProfiles[i];
558 const bool sizeMatches = !haveRequest
559 || (
int(p->size.width) == requestedSize.width()
560 &&
int(p->size.height) == requestedSize.height());
561 const bool formatMatches = !haveRequest
562 || requestedPixel == QVideoFrameFormat::Format_Invalid
563 || requestedPixel == pixelFormatFor(p->format);
564 if (sizeMatches && formatMatches) {
568 if (p->format == CAMERA_FORMAT_YUV_420_SP && p->size.width == 1280
569 && p->size.height == 720)
575 if (!previewProfile) {
576 qCWarning(qLcOhosMediaPlugin) <<
"No preview profile available";
578 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
582 if (OH_CameraManager_CreateCameraInput(m_manager, device, &m_cameraInput) != CAMERA_OK
584 qCWarning(qLcOhosMediaPlugin) <<
"CreateCameraInput failed";
585 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
593 Camera_ErrorCode err = CAMERA_OK;
594 constexpr int kMaxAttempts = 20;
595 constexpr int kBackoffMs = 50;
596 for (
int attempt = 0; attempt < kMaxAttempts; ++attempt) {
597 err = OH_CameraInput_Open(m_cameraInput);
598 if (err == CAMERA_OK)
603 if (err == CAMERA_OPERATION_NOT_ALLOWED) {
604 QSet<QOhosCameraSession *> snapshot;
606 QMutexLocker lock{ &liveSessionMutex() };
607 snapshot = liveSessions();
609 for (
auto *other : snapshot) {
611 other->stopSession();
613 }
else if (err != CAMERA_CONFLICT_CAMERA && err != CAMERA_DEVICE_PREEMPTED) {
616 QThread::msleep(kBackoffMs);
618 if (err != CAMERA_OK) {
619 qCWarning(qLcOhosMediaPlugin) <<
"CameraInput_Open failed:" << err;
620 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
624 QMutexLocker lock{ &liveSessionMutex() };
628 if (m_videoOutput && !previewSurfaceId.isEmpty()) {
629 m_videoOutput->setVideoSize(
630 QSize{
int(previewProfile->size.width),
int(previewProfile->size.height) });
631 if (OH_CameraManager_CreatePreviewOutput(m_manager, previewProfile,
632 previewSurfaceId.constData(), &m_previewOutput)
634 || !m_previewOutput) {
635 qCWarning(qLcOhosMediaPlugin) <<
"CreatePreviewOutput failed";
636 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
642 const bool hasPhoto = createPhotoPath(caps, previewProfile);
644 qCWarning(qLcOhosMediaPlugin) <<
"Photo output unavailable; image capture disabled";
646 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
648 if (!m_previewOutput && !m_photoOutput) {
649 qCWarning(qLcOhosMediaPlugin) <<
"No capture outputs available";
654 if (OH_CameraManager_CreateCaptureSession(m_manager, &m_captureSession) != CAMERA_OK
655 || !m_captureSession) {
656 qCWarning(qLcOhosMediaPlugin) <<
"CreateCaptureSession failed";
661 OH_CaptureSession_BeginConfig(m_captureSession);
662 OH_CaptureSession_AddInput(m_captureSession, m_cameraInput);
664 OH_CaptureSession_AddPreviewOutput(m_captureSession, m_previewOutput);
666 OH_CaptureSession_AddPhotoOutput(m_captureSession, m_photoOutput);
667 if (OH_CaptureSession_CommitConfig(m_captureSession) != CAMERA_OK) {
668 qCWarning(qLcOhosMediaPlugin) <<
"CommitConfig failed";
673 if (OH_CaptureSession_Start(m_captureSession) != CAMERA_OK) {
674 qCWarning(qLcOhosMediaPlugin) <<
"CaptureSession_Start failed";
684 if (m_captureSession) {
685 OH_CaptureSession_Stop(m_captureSession);
695 if (m_captureSession) {
696 OH_CaptureSession_Release(m_captureSession);
697 m_captureSession =
nullptr;
699 if (m_previewOutput) {
700 OH_PreviewOutput_Release(m_previewOutput);
701 m_previewOutput =
nullptr;
704 OH_CameraInput_Close(m_cameraInput);
705 OH_CameraInput_Release(m_cameraInput);
706 m_cameraInput =
nullptr;
708 QMutexLocker lock{ &liveSessionMutex() };
714OH_AVRecorder_CodecMimeType videoCodecToOhos(QMediaFormat::VideoCodec codec)
717 case QMediaFormat::VideoCodec::H265:
718 return AVRECORDER_VIDEO_HEVC;
719 case QMediaFormat::VideoCodec::MPEG4:
720 return AVRECORDER_VIDEO_MPEG4;
721 case QMediaFormat::VideoCodec::H264:
722 case QMediaFormat::VideoCodec::Unspecified:
724 return AVRECORDER_VIDEO_AVC;
728OH_AVRecorder_CodecMimeType audioCodecToOhos(QMediaFormat::AudioCodec codec)
731 case QMediaFormat::AudioCodec::MP3:
732 return AVRECORDER_AUDIO_MP3;
733 case QMediaFormat::AudioCodec::AAC:
734 case QMediaFormat::AudioCodec::Unspecified:
736 return AVRECORDER_AUDIO_AAC;
740OH_AVRecorder_ContainerFormatType containerToOhos(QMediaFormat::FileFormat fmt)
743 case QMediaFormat::AAC:
744 return AVRECORDER_CFT_AAC;
745 case QMediaFormat::MP3:
746 return AVRECORDER_CFT_MP3;
747 case QMediaFormat::Wave:
748 return AVRECORDER_CFT_WAV;
749 case QMediaFormat::Mpeg4Audio:
750 return AVRECORDER_CFT_MPEG_4A;
751 case QMediaFormat::MPEG4:
752 case QMediaFormat::UnspecifiedFormat:
754 return AVRECORDER_CFT_MPEG_4;
758int qualityToVideoBitrate(QMediaRecorder::Quality q,
const QSize &resolution)
760 const int pixels = qMax(1, resolution.width() * resolution.height());
761 const double bpp = [&]() {
763 case QMediaRecorder::VeryLowQuality:
return 0.05;
764 case QMediaRecorder::LowQuality:
return 0.1;
765 case QMediaRecorder::HighQuality:
return 0.25;
766 case QMediaRecorder::VeryHighQuality:
return 0.4;
767 case QMediaRecorder::NormalQuality:
768 default:
return 0.15;
771 return int(pixels * 30 * bpp);
774int qualityToAudioBitrate(QMediaRecorder::Quality q)
777 case QMediaRecorder::VeryLowQuality:
return 32000;
778 case QMediaRecorder::LowQuality:
return 64000;
779 case QMediaRecorder::HighQuality:
return 192000;
780 case QMediaRecorder::VeryHighQuality:
return 256000;
781 case QMediaRecorder::NormalQuality:
782 default:
return 128000;
789 Camera_VideoProfile *out)
791 if (!m_manager || !out)
793 Camera_Device *device = findDevice(m_cameraDevice.id());
794 if (!device && m_supportedDeviceCount > 0)
795 device = &m_supportedDevices[0];
799 Camera_OutputCapability *caps =
nullptr;
800 if (OH_CameraManager_GetSupportedCameraOutputCapability(m_manager, device, &caps) != CAMERA_OK
801 || !caps || caps->videoProfilesSize == 0) {
803 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
807 Camera_VideoProfile *chosen =
nullptr;
808 const QSize wanted = settings.videoResolution();
809 if (wanted.isValid()) {
810 for (uint32_t i = 0; i < caps->videoProfilesSize; ++i) {
811 Camera_VideoProfile *p = caps->videoProfiles[i];
814 if (
int(p->size.width) == wanted.width()
815 &&
int(p->size.height) == wanted.height()) {
822 for (uint32_t i = 0; i < caps->videoProfilesSize; ++i) {
823 Camera_VideoProfile *p = caps->videoProfiles[i];
826 if (p->size.width == 1280 && p->size.height == 720
827 && p->format == CAMERA_FORMAT_YUV_420_SP) {
834 chosen = caps->videoProfiles[0];
836 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
841 const QByteArray &surfaceId)
843 if (!m_captureSession)
845 if (OH_CameraManager_CreateVideoOutput(m_manager, &profile, surfaceId.constData(),
846 &m_videoOutputCamera) != CAMERA_OK
847 || !m_videoOutputCamera) {
851 OH_CaptureSession_Stop(m_captureSession);
852 OH_CaptureSession_BeginConfig(m_captureSession);
853 OH_CaptureSession_AddVideoOutput(m_captureSession, m_videoOutputCamera);
854 if (OH_CaptureSession_CommitConfig(m_captureSession) != CAMERA_OK) {
855 OH_VideoOutput_Release(m_videoOutputCamera);
856 m_videoOutputCamera =
nullptr;
857 OH_CaptureSession_Start(m_captureSession);
860 if (OH_CaptureSession_Start(m_captureSession) != CAMERA_OK)
862 if (OH_VideoOutput_Start(m_videoOutputCamera) != CAMERA_OK)
869 if (!m_videoOutputCamera)
871 OH_VideoOutput_Stop(m_videoOutputCamera);
872 if (m_captureSession) {
873 OH_CaptureSession_Stop(m_captureSession);
874 OH_CaptureSession_BeginConfig(m_captureSession);
875 OH_CaptureSession_RemoveVideoOutput(m_captureSession, m_videoOutputCamera);
876 OH_CaptureSession_CommitConfig(m_captureSession);
877 OH_CaptureSession_Start(m_captureSession);
879 OH_VideoOutput_Release(m_videoOutputCamera);
880 m_videoOutputCamera =
nullptr;
884 OH_AVRecorder_State state,
885 OH_AVRecorder_StateChangeReason ,
891 QMetaObject::invokeMethod(self,
"onRecorderStateNotification", Qt::QueuedConnection,
892 Q_ARG(
int,
int(state)));
896 const char *errorMsg,
void *userData)
901 QMetaObject::invokeMethod(self,
"onRecorderErrorNotification", Qt::QueuedConnection,
902 Q_ARG(
int, errorCode),
903 Q_ARG(QString, QString::fromUtf8(errorMsg ? errorMsg :
"")));
908 QMediaRecorder::RecorderState mapped = m_recorderState;
910 case AVRECORDER_STARTED:
911 mapped = QMediaRecorder::RecordingState;
913 case AVRECORDER_PAUSED:
914 mapped = QMediaRecorder::PausedState;
916 case AVRECORDER_STOPPED:
917 case AVRECORDER_IDLE:
918 case AVRECORDER_RELEASED:
919 mapped = QMediaRecorder::StoppedState;
921 case AVRECORDER_ERROR:
922 mapped = QMediaRecorder::StoppedState;
927 if (mapped == m_recorderState)
929 m_recorderState = mapped;
930 emit recorderStateChanged(
int(mapped));
935 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
937 ? tr(
"Recorder error %1").arg(code)
943 if (m_recorderState == QMediaRecorder::StoppedState)
945 if (m_recorderState == QMediaRecorder::PausedState)
946 return m_recorderPausedMs;
947 if (!m_recorderTimer.isValid())
948 return m_recorderPausedMs;
949 return m_recorderPausedMs + (m_recorderTimer.elapsed() - m_recorderResumeStartMs);
953 const QString &location)
956 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
957 tr(
"Recording already in progress"));
963 const bool videoEnabled = m_active && m_captureSession;
964 Camera_VideoProfile videoProfile{};
966 if (!findVideoProfile(settings, &videoProfile)) {
967 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
968 tr(
"No matching camera video profile"));
973 m_recorder = OH_AVRecorder_Create();
975 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
976 tr(
"OH_AVRecorder_Create failed"));
980 OH_AVRecorder_SetStateCallback(m_recorder, recorderStateCallback,
this);
981 OH_AVRecorder_SetErrorCallback(m_recorder, recorderErrorCallback,
this);
983 QString resolved = location;
984 if (QFileInfo(resolved).suffix().isEmpty()) {
985 const QString suffix = settings.preferredSuffix();
986 if (!suffix.isEmpty())
987 resolved.append(QLatin1Char(
'.')).append(suffix);
989 resolved.append(QStringLiteral(
".mp4"));
992 QByteArray urlBytes = QStringLiteral(
"fd://").toUtf8();
993 int fd = ::open(QFile::encodeName(resolved).constData(),
994 O_RDWR | O_CREAT | O_TRUNC, 0644);
996 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
997 tr(
"Could not open output file: %1").arg(resolved));
1001 urlBytes.append(QByteArray::number(fd));
1003 OH_AVRecorder_Config config{};
1004 config.audioSourceType = AVRECORDER_MIC;
1005 config.profile.audioBitrate = settings.audioBitRate() > 0
1006 ? settings.audioBitRate() : qualityToAudioBitrate(settings.quality());
1007 config.profile.audioChannels = settings.audioChannelCount() > 0
1008 ? settings.audioChannelCount() : 2;
1009 config.profile.audioCodec = audioCodecToOhos(settings.audioCodec());
1010 config.profile.audioSampleRate = settings.audioSampleRate() > 0
1011 ? settings.audioSampleRate() : 48000;
1012 config.profile.fileFormat = containerToOhos(settings.fileFormat());
1014 const QSize videoSize{
int(videoProfile.size.width),
int(videoProfile.size.height) };
1015 config.videoSourceType = AVRECORDER_SURFACE_YUV;
1016 config.profile.videoBitrate = settings.videoBitRate() > 0
1017 ? settings.videoBitRate() : qualityToVideoBitrate(settings.quality(), videoSize);
1018 config.profile.videoCodec = videoCodecToOhos(settings.videoCodec());
1019 config.profile.videoFrameWidth = videoSize.width();
1020 config.profile.videoFrameHeight = videoSize.height();
1021 config.profile.videoFrameRate = settings.videoFrameRate() > 0
1022 ?
int(settings.videoFrameRate()) : 30;
1024 config.profile.isHdr =
false;
1025 config.profile.enableTemporalScale =
false;
1026 config.url =
const_cast<
char *>(urlBytes.constData());
1027 config.fileGenerationMode = AVRECORDER_APP_CREATE;
1028 config.maxDuration = 0;
1030 if (OH_AVRecorder_Prepare(m_recorder, &config) != AV_ERR_OK) {
1031 emit recorderErrorOccurred(
int(QMediaRecorder::FormatError),
1032 tr(
"OH_AVRecorder_Prepare failed"));
1039 if (OH_AVRecorder_GetInputSurface(m_recorder, &m_recorderWindow) != AV_ERR_OK
1040 || !m_recorderWindow) {
1041 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1042 tr(
"OH_AVRecorder_GetInputSurface failed"));
1047 uint64_t surfaceIdNum = 0;
1048 if (OH_NativeWindow_GetSurfaceId(m_recorderWindow, &surfaceIdNum) != 0
1049 || surfaceIdNum == 0) {
1050 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1051 tr(
"Failed to obtain recorder surface ID"));
1055 const QByteArray surfaceId = QByteArray::number(qulonglong(surfaceIdNum));
1057 if (!attachVideoOutput(videoProfile, surfaceId)) {
1058 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1059 tr(
"Failed to attach video output to capture session"));
1065 if (OH_AVRecorder_Start(m_recorder) != AV_ERR_OK) {
1066 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1067 tr(
"OH_AVRecorder_Start failed"));
1068 detachVideoOutput();
1073 m_recorderActualLocation = QUrl::fromLocalFile(resolved);
1074 emit recorderActualLocationChanged(m_recorderActualLocation);
1075 m_recorderPausedMs = 0;
1076 m_recorderResumeStartMs = 0;
1077 m_recorderTimer.restart();
1083 if (!m_recorder || m_recorderState != QMediaRecorder::RecordingState)
1085 if (OH_AVRecorder_Pause(m_recorder) == AV_ERR_OK) {
1086 m_recorderPausedMs += (m_recorderTimer.elapsed() - m_recorderResumeStartMs);
1092 if (!m_recorder || m_recorderState != QMediaRecorder::PausedState)
1094 if (OH_AVRecorder_Resume(m_recorder) == AV_ERR_OK)
1095 m_recorderResumeStartMs = m_recorderTimer.elapsed();
1102 OH_AVRecorder_Stop(m_recorder);
1103 detachVideoOutput();
1110 OH_AVRecorder_Release(m_recorder);
1111 m_recorder =
nullptr;
1113 m_recorderWindow =
nullptr;
1114 if (m_recorderState != QMediaRecorder::StoppedState) {
1115 m_recorderState = QMediaRecorder::StoppedState;
1116 emit recorderStateChanged(
int(QMediaRecorder::StoppedState));
1118 m_recorderTimer.invalidate();
1119 m_recorderPausedMs = 0;
1120 m_recorderResumeStartMs = 0;
1125#include "moc_qohoscamerasession_p.cpp"
int capture(const QString &fileName, bool toBuffer=false)
bool isReadyForCapture() const
void setCamera(const QCameraDevice &camera)
qint64 recorderDuration() const
void setActive(bool active)
void onRecorderStateNotification(int state)
void setCameraFormat(const QCameraFormat &format)
void onRecorderErrorNotification(int code, const QString &message)
~QOhosCameraSession() override
bool startRecording(const QMediaEncoderSettings &settings, const QString &location)
void setImageSettings(const QImageEncoderSettings &settings)
QVideoFrameFormat::PixelFormat pixelFormatFor(Camera_Format format)
int qualityToInt(QImageCapture::Quality q)
QByteArray encodeNativeImageToJpeg(OH_ImageNative *image, int quality)
constexpr int32_t kImageReceiverCapacity
void imageArriveCallbackTrampoline(OH_ImageReceiverNative *, void *userData)
constexpr const char * kJpegMimeType
QSet< QOhosCameraSession * > & liveSessions()
QRecursiveMutex & liveSessionMutex()