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/qguiapplication.h>
19#include <QtGui/qimage.h>
20#include <QtMultimedia/qvideosink.h>
25#include <private/qcameradevice_p.h>
26#include <private/qmediastoragelocation_p.h>
27#include <private/qmemoryvideobuffer_p.h>
28#include <private/qvideoframe_p.h>
30#include <multimedia/image_framework/image/image_native.h>
31#include <multimedia/image_framework/image/image_packer_native.h>
32#include <multimedia/image_framework/image/image_source_native.h>
33#include <native_buffer/native_buffer.h>
34#include <native_window/external_window.h>
54 static QRecursiveMutex m;
59 static QSet<QOhosCameraSession *> s;
66 case CAMERA_FORMAT_RGBA_8888:
67 return QVideoFrameFormat::Format_RGBA8888;
68 case CAMERA_FORMAT_YUV_420_SP:
69 return QVideoFrameFormat::Format_NV12;
70 case CAMERA_FORMAT_JPEG:
71 return QVideoFrameFormat::Format_Jpeg;
75 return QVideoFrameFormat::Format_Invalid;
81 case QImageCapture::VeryLowQuality:
83 case QImageCapture::LowQuality:
85 case QImageCapture::HighQuality:
87 case QImageCapture::VeryHighQuality:
89 case QImageCapture::NormalQuality:
97 uint32_t *components =
nullptr;
98 size_t componentCount = 0;
99 if (OH_ImageNative_GetComponentTypes(image,
nullptr, &componentCount) != IMAGE_SUCCESS
100 || componentCount == 0) {
103 std::vector<uint32_t> types(componentCount);
104 components = types.data();
105 if (OH_ImageNative_GetComponentTypes(image, &components, &componentCount) != IMAGE_SUCCESS)
108 OH_NativeBuffer *nativeBuffer =
nullptr;
109 if (OH_ImageNative_GetByteBuffer(image, types[0], &nativeBuffer) != IMAGE_SUCCESS
114 OH_NativeBuffer_Config bufferConfig{};
115 OH_NativeBuffer_GetConfig(nativeBuffer, &bufferConfig);
117 void *mapped =
nullptr;
118 if (OH_NativeBuffer_Map(nativeBuffer, &mapped) != 0 || !mapped)
121 size_t bufferSize = 0;
122 OH_ImageNative_GetBufferSize(image, types[0], &bufferSize);
125 OH_ImageSourceNative *source =
nullptr;
126 if (OH_ImageSourceNative_CreateFromData(
static_cast<uint8_t *>(mapped), bufferSize, &source)
129 OH_ImagePackerNative *packer =
nullptr;
130 if (OH_ImagePackerNative_Create(&packer) == IMAGE_SUCCESS && packer) {
131 OH_PackingOptions *options =
nullptr;
132 if (OH_PackingOptions_Create(&options) == IMAGE_SUCCESS && options) {
133 Image_MimeType mime{
const_cast<
char *>(kJpegMimeType),
134 std::strlen(kJpegMimeType) };
135 OH_PackingOptions_SetMimeType(options, &mime);
136 OH_PackingOptions_SetQuality(options, uint32_t(quality));
138 size_t outSize = bufferSize * 2 + 1024;
139 result.resize(
int(outSize));
140 if (OH_ImagePackerNative_PackToDataFromImageSource(
141 packer, options, source,
142 reinterpret_cast<uint8_t *>(result.data()), &outSize)
144 result.resize(
int(outSize));
148 OH_PackingOptions_Release(options);
150 OH_ImagePackerNative_Release(packer);
152 OH_ImageSourceNative_Release(source);
155 OH_NativeBuffer_Unmap(nativeBuffer);
164 QMetaObject::invokeMethod(session, &QOhosCameraSession::onCapturedImageAvailable,
165 Qt::QueuedConnection);
173 connect(qApp, &QGuiApplication::applicationStateChanged,
this,
182 if (QGuiApplication::applicationState() == Qt::ApplicationActive) {
183 if (m_restoreOnForeground) {
184 m_restoreOnForeground =
false;
187 }
else if (m_active) {
189 m_restoreOnForeground =
true;
196 QMutexLocker lock{ &liveSessionMutex() };
200 if (m_supportedDevices) {
201 OH_CameraManager_DeleteSupportedCameras(m_manager, m_supportedDevices,
202 m_supportedDeviceCount);
203 m_supportedDevices =
nullptr;
206 OH_Camera_DeleteCameraManager(m_manager);
213 if (m_cameraDevice == camera)
215 const bool wasActive = m_active;
218 m_cameraDevice = camera;
225 m_cameraFormat = format;
230 if (m_videoSink == sink)
234 m_videoOutput.reset();
235 m_videoOutput = std::make_unique<QOhosVideoOutput>(
236 sink, QOhosVideoOutput::ContentSource::Camera,
this);
237 connect(m_videoOutput.get(), &QOhosVideoOutput::surfaceReady,
this,
238 &QOhosCameraSession::onSurfaceReady);
243 if (m_active == active)
248 if (active && qApp && QGuiApplication::applicationState() != Qt::ApplicationActive) {
249 m_restoreOnForeground =
true;
252 m_restoreOnForeground =
false;
255 if (!startSession()) {
256 m_pendingStart =
true;
260 m_pendingStart =
false;
264 emit activeChanged(active);
265 emitReadyForCaptureChanged();
270 m_imageSettings = settings;
275 const int id = ++m_lastCaptureId;
276 if (!m_active || !m_photoOutput) {
277 emit imageCaptureError(id, QImageCapture::NotReadyError,
278 tr(
"Camera not ready for capture"));
281 if (m_captureInProgress) {
282 emit imageCaptureError(id, QImageCapture::NotReadyError,
283 tr(
"Capture already in progress"));
287 m_pendingCaptureId = id;
288 m_pendingCaptureFileName = fileName;
289 m_pendingCaptureToBuffer = toBuffer;
290 m_captureInProgress =
true;
291 emitReadyForCaptureChanged();
293 if (OH_PhotoOutput_Capture(m_photoOutput) != CAMERA_OK) {
294 m_captureInProgress =
false;
295 emit imageCaptureError(id, QImageCapture::ResourceError,
296 tr(
"OH_PhotoOutput_Capture failed"));
297 emitReadyForCaptureChanged();
304 if (m_pendingStart && !m_active) {
305 m_pendingStart =
false;
306 if (startSession()) {
308 emit activeChanged(
true);
309 emitReadyForCaptureChanged();
315 if (!m_active || !m_videoOutput)
317 const QByteArray surfaceId = m_videoOutput->surfaceId();
318 if (surfaceId.isEmpty() || surfaceId == m_previewSurfaceId)
323 if (startSession()) {
325 emit activeChanged(
true);
326 emitReadyForCaptureChanged();
332 if (!m_imageReceiver)
335 OH_ImageNative *image =
nullptr;
336 if (OH_ImageReceiverNative_ReadLatestImage(m_imageReceiver, &image) != IMAGE_SUCCESS
338 m_captureInProgress =
false;
339 emit imageCaptureError(m_pendingCaptureId, QImageCapture::ResourceError,
340 tr(
"Failed to read captured image"));
341 emitReadyForCaptureChanged();
345 const int quality = qualityToInt(m_imageSettings.quality());
346 QByteArray jpegBytes = encodeNativeImageToJpeg(image, quality);
347 OH_ImageNative_Release(image);
349 const int id = m_pendingCaptureId;
350 const QString fileName = m_pendingCaptureFileName;
351 const bool toBuffer = m_pendingCaptureToBuffer;
352 m_pendingCaptureFileName.clear();
353 m_pendingCaptureToBuffer =
false;
354 m_pendingCaptureId = 0;
355 m_captureInProgress =
false;
357 if (jpegBytes.isEmpty()) {
358 emit imageCaptureError(id, QImageCapture::FormatError,
359 tr(
"Failed to encode captured image"));
360 emitReadyForCaptureChanged();
364 QImage preview = QImage::fromData(jpegBytes,
"JPEG");
365 emit imageExposed(id);
366 emit imageCaptured(id, preview);
369 QVideoFrame buffer = QVideoFramePrivate::createFrame(
370 std::make_unique<QMemoryVideoBuffer>(QByteArray(jpegBytes),
371 preview.bytesPerLine()),
372 QVideoFrameFormat(preview.size(), QVideoFrameFormat::Format_Jpeg));
373 emit imageAvailable(id, buffer);
374 emitReadyForCaptureChanged();
381 const QImageCapture::FileFormat targetFormat = m_imageSettings.format();
382 const QString defaultExt = [&]() -> QString {
383 switch (targetFormat) {
384 case QImageCapture::PNG:
return QStringLiteral(
"png");
385 case QImageCapture::WebP:
return QStringLiteral(
"webp");
386 case QImageCapture::Tiff:
return QStringLiteral(
"tiff");
387 case QImageCapture::JPEG:
388 case QImageCapture::UnspecifiedFormat:
389 default:
return QStringLiteral(
"jpg");
392 const char *qImageFormat = [&]() ->
const char * {
393 switch (targetFormat) {
394 case QImageCapture::PNG:
return "PNG";
395 case QImageCapture::WebP:
return "WEBP";
396 case QImageCapture::Tiff:
return "TIFF";
397 default:
return nullptr;
400 const QString resolved = QMediaStorageLocation::generateFileName(
401 fileName, QStandardPaths::PicturesLocation, defaultExt);
404 saved = preview.save(resolved, qImageFormat, quality);
407 if (out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
408 out.write(jpegBytes);
414 emit imageSaved(id, resolved);
416 emit imageCaptureError(id, QImageCapture::ResourceError,
417 tr(
"Could not save captured image to: %1").arg(resolved));
420 emitReadyForCaptureChanged();
426 if (m_lastReadyForCapture && *m_lastReadyForCapture == ready)
428 m_lastReadyForCapture = ready;
429 emit readyForCaptureChanged(ready);
436 if (OH_Camera_GetCameraManager(&m_manager) != CAMERA_OK || !m_manager) {
437 qCWarning(qLcOhosMediaPlugin) <<
"OH_Camera_GetCameraManager failed";
440 if (OH_CameraManager_GetSupportedCameras(m_manager, &m_supportedDevices,
441 &m_supportedDeviceCount) != CAMERA_OK) {
442 qCWarning(qLcOhosMediaPlugin) <<
"GetSupportedCameras failed";
443 m_supportedDevices =
nullptr;
444 m_supportedDeviceCount = 0;
451 if (!m_supportedDevices)
453 for (uint32_t i = 0; i < m_supportedDeviceCount; ++i) {
454 if (m_supportedDevices[i].cameraId && id == m_supportedDevices[i].cameraId)
455 return &m_supportedDevices[i];
461 Camera_Profile *previewProfile)
463 if (!caps || caps->photoProfilesSize == 0)
466 Camera_Profile *photoProfile = caps->photoProfiles[0];
467 if (m_imageSettings.resolution().isValid()) {
468 const QSize wanted = m_imageSettings.resolution();
469 for (uint32_t i = 0; i < caps->photoProfilesSize; ++i) {
470 Camera_Profile *p = caps->photoProfiles[i];
473 if (
int(p->size.width) == wanted.width()
474 &&
int(p->size.height) == wanted.height()) {
479 }
else if (previewProfile) {
480 for (uint32_t i = 0; i < caps->photoProfilesSize; ++i) {
481 Camera_Profile *p = caps->photoProfiles[i];
484 if (p->size.width == previewProfile->size.width
485 && p->size.height == previewProfile->size.height) {
494 if (OH_ImageReceiverOptions_Create(&m_imageReceiverOptions) != IMAGE_SUCCESS
495 || !m_imageReceiverOptions) {
498 Image_Size size{ uint32_t(photoProfile->size.width), uint32_t(photoProfile->size.height) };
499 OH_ImageReceiverOptions_SetSize(m_imageReceiverOptions, size);
502 if (OH_ImageReceiverNative_Create(m_imageReceiverOptions, &m_imageReceiver) != IMAGE_SUCCESS
503 || !m_imageReceiver) {
507 OH_ImageReceiverNative_OnImageArrive(m_imageReceiver, imageArriveCallbackTrampoline,
this);
509 uint64_t surfaceIdNum = 0;
510 if (OH_ImageReceiverNative_GetReceivingSurfaceId(m_imageReceiver, &surfaceIdNum) != IMAGE_SUCCESS
511 || surfaceIdNum == 0) {
514 const QByteArray surfaceId = QByteArray::number(qulonglong(surfaceIdNum));
516 if (OH_CameraManager_CreatePhotoOutput(m_manager, photoProfile, surfaceId.constData(),
517 &m_photoOutput) != CAMERA_OK
527 OH_PhotoOutput_Release(m_photoOutput);
528 m_photoOutput =
nullptr;
530 if (m_imageReceiver) {
531 OH_ImageReceiverNative_OffImageArrive(m_imageReceiver, imageArriveCallbackTrampoline);
532 OH_ImageReceiverNative_Release(m_imageReceiver);
533 m_imageReceiver =
nullptr;
535 if (m_imageReceiverOptions) {
536 OH_ImageReceiverOptions_Release(m_imageReceiverOptions);
537 m_imageReceiverOptions =
nullptr;
547 QMutexLocker lock{ &liveSessionMutex() };
548 const auto sessions = liveSessions();
549 for (
auto *other : sessions) {
551 other->stopSession();
559 if (!m_videoOutput) {
560 m_videoOutput = std::make_unique<QOhosVideoOutput>(
561 nullptr, QOhosVideoOutput::ContentSource::Camera,
this);
562 connect(m_videoOutput.get(), &QOhosVideoOutput::surfaceReady,
this,
563 &QOhosCameraSession::onSurfaceReady);
565 m_previewSurfaceId = m_videoOutput->surfaceId();
567 if (!ensureManager())
570 Camera_Device *device = findDevice(m_cameraDevice.id());
571 if (!device && m_supportedDeviceCount > 0)
572 device = &m_supportedDevices[0];
574 qCWarning(qLcOhosMediaPlugin) <<
"No camera device available";
578 Camera_Profile *previewProfile =
nullptr;
579 Camera_OutputCapability *caps =
nullptr;
580 if (OH_CameraManager_GetSupportedCameraOutputCapability(m_manager, device, &caps) == CAMERA_OK
581 && caps && caps->previewProfilesSize > 0) {
582 previewProfile = caps->previewProfiles[0];
583 const bool haveRequest = !m_cameraFormat.isNull();
584 const QSize requestedSize = haveRequest ? m_cameraFormat.resolution() : QSize{};
585 const QVideoFrameFormat::PixelFormat requestedPixel =
586 haveRequest ? m_cameraFormat.pixelFormat() : QVideoFrameFormat::Format_Invalid;
587 for (uint32_t i = 0; i < caps->previewProfilesSize; ++i) {
588 Camera_Profile *p = caps->previewProfiles[i];
591 const bool sizeMatches = !haveRequest
592 || (
int(p->size.width) == requestedSize.width()
593 &&
int(p->size.height) == requestedSize.height());
594 const bool formatMatches = !haveRequest
595 || requestedPixel == QVideoFrameFormat::Format_Invalid
596 || requestedPixel == pixelFormatFor(p->format);
597 if (sizeMatches && formatMatches) {
601 if (p->format == CAMERA_FORMAT_YUV_420_SP && p->size.width == 1280
602 && p->size.height == 720)
608 if (!previewProfile) {
609 qCWarning(qLcOhosMediaPlugin) <<
"No preview profile available";
611 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
615 if (OH_CameraManager_CreateCameraInput(m_manager, device, &m_cameraInput) != CAMERA_OK
617 qCWarning(qLcOhosMediaPlugin) <<
"CreateCameraInput failed";
618 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
626 Camera_ErrorCode err = CAMERA_OK;
627 constexpr int kMaxAttempts = 20;
628 constexpr int kBackoffMs = 50;
629 for (
int attempt = 0; attempt < kMaxAttempts; ++attempt) {
630 err = OH_CameraInput_Open(m_cameraInput);
631 if (err == CAMERA_OK)
636 if (err == CAMERA_OPERATION_NOT_ALLOWED) {
637 QSet<QOhosCameraSession *> snapshot;
639 QMutexLocker lock{ &liveSessionMutex() };
640 snapshot = liveSessions();
642 for (
auto *other : snapshot) {
644 other->stopSession();
646 }
else if (err != CAMERA_CONFLICT_CAMERA && err != CAMERA_DEVICE_PREEMPTED) {
649 QThread::msleep(kBackoffMs);
651 if (err != CAMERA_OK) {
652 qCWarning(qLcOhosMediaPlugin) <<
"CameraInput_Open failed:" << err;
653 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
657 QMutexLocker lock{ &liveSessionMutex() };
661 if (m_videoOutput && !m_previewSurfaceId.isEmpty()) {
662 m_videoOutput->setVideoSize(
663 QSize{
int(previewProfile->size.width),
int(previewProfile->size.height) });
664 if (OH_CameraManager_CreatePreviewOutput(m_manager, previewProfile,
665 m_previewSurfaceId.constData(), &m_previewOutput)
667 || !m_previewOutput) {
668 qCWarning(qLcOhosMediaPlugin) <<
"CreatePreviewOutput failed";
669 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
675 const bool hasPhoto = createPhotoPath(caps, previewProfile);
677 qCWarning(qLcOhosMediaPlugin) <<
"Photo output unavailable; image capture disabled";
679 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
681 if (!m_previewOutput && !m_photoOutput) {
682 qCWarning(qLcOhosMediaPlugin) <<
"No capture outputs available";
687 if (OH_CameraManager_CreateCaptureSession(m_manager, &m_captureSession) != CAMERA_OK
688 || !m_captureSession) {
689 qCWarning(qLcOhosMediaPlugin) <<
"CreateCaptureSession failed";
694 OH_CaptureSession_BeginConfig(m_captureSession);
695 OH_CaptureSession_AddInput(m_captureSession, m_cameraInput);
697 OH_CaptureSession_AddPreviewOutput(m_captureSession, m_previewOutput);
699 OH_CaptureSession_AddPhotoOutput(m_captureSession, m_photoOutput);
700 if (OH_CaptureSession_CommitConfig(m_captureSession) != CAMERA_OK) {
701 qCWarning(qLcOhosMediaPlugin) <<
"CommitConfig failed";
706 if (OH_CaptureSession_Start(m_captureSession) != CAMERA_OK) {
707 qCWarning(qLcOhosMediaPlugin) <<
"CaptureSession_Start failed";
717 if (m_captureSession) {
718 OH_CaptureSession_Stop(m_captureSession);
728 if (m_captureSession) {
729 OH_CaptureSession_Release(m_captureSession);
730 m_captureSession =
nullptr;
732 if (m_previewOutput) {
733 OH_PreviewOutput_Release(m_previewOutput);
734 m_previewOutput =
nullptr;
736 m_previewSurfaceId.clear();
738 OH_CameraInput_Close(m_cameraInput);
739 OH_CameraInput_Release(m_cameraInput);
740 m_cameraInput =
nullptr;
742 QMutexLocker lock{ &liveSessionMutex() };
748OH_AVRecorder_CodecMimeType videoCodecToOhos(QMediaFormat::VideoCodec codec)
751 case QMediaFormat::VideoCodec::H265:
752 return AVRECORDER_VIDEO_HEVC;
753 case QMediaFormat::VideoCodec::MPEG4:
754 return AVRECORDER_VIDEO_MPEG4;
755 case QMediaFormat::VideoCodec::H264:
756 case QMediaFormat::VideoCodec::Unspecified:
758 return AVRECORDER_VIDEO_AVC;
762OH_AVRecorder_CodecMimeType audioCodecToOhos(QMediaFormat::AudioCodec codec)
765 case QMediaFormat::AudioCodec::MP3:
766 return AVRECORDER_AUDIO_MP3;
767 case QMediaFormat::AudioCodec::AAC:
768 case QMediaFormat::AudioCodec::Unspecified:
770 return AVRECORDER_AUDIO_AAC;
774OH_AVRecorder_ContainerFormatType containerToOhos(QMediaFormat::FileFormat fmt)
777 case QMediaFormat::AAC:
778 return AVRECORDER_CFT_AAC;
779 case QMediaFormat::MP3:
780 return AVRECORDER_CFT_MP3;
781 case QMediaFormat::Wave:
782 return AVRECORDER_CFT_WAV;
783 case QMediaFormat::Mpeg4Audio:
784 return AVRECORDER_CFT_MPEG_4A;
785 case QMediaFormat::MPEG4:
786 case QMediaFormat::UnspecifiedFormat:
788 return AVRECORDER_CFT_MPEG_4;
792int qualityToVideoBitrate(QMediaRecorder::Quality q,
const QSize &resolution)
794 const int pixels = qMax(1, resolution.width() * resolution.height());
795 const double bpp = [&]() {
797 case QMediaRecorder::VeryLowQuality:
return 0.05;
798 case QMediaRecorder::LowQuality:
return 0.1;
799 case QMediaRecorder::HighQuality:
return 0.25;
800 case QMediaRecorder::VeryHighQuality:
return 0.4;
801 case QMediaRecorder::NormalQuality:
802 default:
return 0.15;
805 return int(pixels * 30 * bpp);
808int qualityToAudioBitrate(QMediaRecorder::Quality q)
811 case QMediaRecorder::VeryLowQuality:
return 32000;
812 case QMediaRecorder::LowQuality:
return 64000;
813 case QMediaRecorder::HighQuality:
return 192000;
814 case QMediaRecorder::VeryHighQuality:
return 256000;
815 case QMediaRecorder::NormalQuality:
816 default:
return 128000;
823 Camera_VideoProfile *out)
825 if (!m_manager || !out)
827 Camera_Device *device = findDevice(m_cameraDevice.id());
828 if (!device && m_supportedDeviceCount > 0)
829 device = &m_supportedDevices[0];
833 Camera_OutputCapability *caps =
nullptr;
834 if (OH_CameraManager_GetSupportedCameraOutputCapability(m_manager, device, &caps) != CAMERA_OK
835 || !caps || caps->videoProfilesSize == 0) {
837 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
841 Camera_VideoProfile *chosen =
nullptr;
842 const QSize wanted = settings.videoResolution();
843 if (wanted.isValid()) {
844 for (uint32_t i = 0; i < caps->videoProfilesSize; ++i) {
845 Camera_VideoProfile *p = caps->videoProfiles[i];
848 if (
int(p->size.width) == wanted.width()
849 &&
int(p->size.height) == wanted.height()) {
856 for (uint32_t i = 0; i < caps->videoProfilesSize; ++i) {
857 Camera_VideoProfile *p = caps->videoProfiles[i];
860 if (p->size.width == 1280 && p->size.height == 720
861 && p->format == CAMERA_FORMAT_YUV_420_SP) {
868 chosen = caps->videoProfiles[0];
870 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
875 const QByteArray &surfaceId)
877 if (!m_captureSession)
879 if (OH_CameraManager_CreateVideoOutput(m_manager, &profile, surfaceId.constData(),
880 &m_videoOutputCamera) != CAMERA_OK
881 || !m_videoOutputCamera) {
885 OH_CaptureSession_Stop(m_captureSession);
886 OH_CaptureSession_BeginConfig(m_captureSession);
887 OH_CaptureSession_AddVideoOutput(m_captureSession, m_videoOutputCamera);
888 if (OH_CaptureSession_CommitConfig(m_captureSession) != CAMERA_OK) {
889 OH_VideoOutput_Release(m_videoOutputCamera);
890 m_videoOutputCamera =
nullptr;
891 OH_CaptureSession_Start(m_captureSession);
894 if (OH_CaptureSession_Start(m_captureSession) != CAMERA_OK)
896 if (OH_VideoOutput_Start(m_videoOutputCamera) != CAMERA_OK)
903 if (!m_videoOutputCamera)
905 OH_VideoOutput_Stop(m_videoOutputCamera);
906 if (m_captureSession) {
907 OH_CaptureSession_Stop(m_captureSession);
908 OH_CaptureSession_BeginConfig(m_captureSession);
909 OH_CaptureSession_RemoveVideoOutput(m_captureSession, m_videoOutputCamera);
910 OH_CaptureSession_CommitConfig(m_captureSession);
911 OH_CaptureSession_Start(m_captureSession);
913 OH_VideoOutput_Release(m_videoOutputCamera);
914 m_videoOutputCamera =
nullptr;
918 OH_AVRecorder_State state,
919 OH_AVRecorder_StateChangeReason ,
925 QMetaObject::invokeMethod(self,
"onRecorderStateNotification", Qt::QueuedConnection,
926 Q_ARG(
int,
int(state)));
930 const char *errorMsg,
void *userData)
935 QMetaObject::invokeMethod(self,
"onRecorderErrorNotification", Qt::QueuedConnection,
936 Q_ARG(
int, errorCode),
937 Q_ARG(QString, QString::fromUtf8(errorMsg ? errorMsg :
"")));
942 QMediaRecorder::RecorderState mapped = m_recorderState;
944 case AVRECORDER_STARTED:
945 mapped = QMediaRecorder::RecordingState;
947 case AVRECORDER_PAUSED:
948 mapped = QMediaRecorder::PausedState;
950 case AVRECORDER_STOPPED:
951 case AVRECORDER_IDLE:
952 case AVRECORDER_RELEASED:
953 mapped = QMediaRecorder::StoppedState;
955 case AVRECORDER_ERROR:
956 mapped = QMediaRecorder::StoppedState;
961 if (mapped == m_recorderState)
963 m_recorderState = mapped;
964 emit recorderStateChanged(
int(mapped));
969 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
971 ? tr(
"Recorder error %1").arg(code)
977 if (m_recorderState == QMediaRecorder::StoppedState)
979 if (m_recorderState == QMediaRecorder::PausedState)
980 return m_recorderPausedMs;
981 if (!m_recorderTimer.isValid())
982 return m_recorderPausedMs;
983 return m_recorderPausedMs + (m_recorderTimer.elapsed() - m_recorderResumeStartMs);
987 const QString &location)
990 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
991 tr(
"Recording already in progress"));
997 const bool videoEnabled = m_active && m_captureSession;
998 Camera_VideoProfile videoProfile{};
1000 if (!findVideoProfile(settings, &videoProfile)) {
1001 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1002 tr(
"No matching camera video profile"));
1007 m_recorder = OH_AVRecorder_Create();
1009 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1010 tr(
"OH_AVRecorder_Create failed"));
1014 OH_AVRecorder_SetStateCallback(m_recorder, recorderStateCallback,
this);
1015 OH_AVRecorder_SetErrorCallback(m_recorder, recorderErrorCallback,
this);
1017 QString resolved = location;
1018 if (QFileInfo(resolved).suffix().isEmpty()) {
1019 const QString suffix = settings.preferredSuffix();
1020 if (!suffix.isEmpty())
1021 resolved.append(QLatin1Char(
'.')).append(suffix);
1023 resolved.append(QStringLiteral(
".mp4"));
1026 QByteArray urlBytes = QStringLiteral(
"fd://").toUtf8();
1027 int fd = ::open(QFile::encodeName(resolved).constData(),
1028 O_RDWR | O_CREAT | O_TRUNC, 0644);
1030 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1031 tr(
"Could not open output file: %1").arg(resolved));
1035 urlBytes.append(QByteArray::number(fd));
1037 OH_AVRecorder_Config config{};
1038 config.audioSourceType = AVRECORDER_MIC;
1039 config.profile.audioBitrate = settings.audioBitRate() > 0
1040 ? settings.audioBitRate() : qualityToAudioBitrate(settings.quality());
1041 config.profile.audioChannels = settings.audioChannelCount() > 0
1042 ? settings.audioChannelCount() : 2;
1043 config.profile.audioCodec = audioCodecToOhos(settings.audioCodec());
1044 config.profile.audioSampleRate = settings.audioSampleRate() > 0
1045 ? settings.audioSampleRate() : 48000;
1046 config.profile.fileFormat = containerToOhos(settings.fileFormat());
1048 const QSize videoSize{
int(videoProfile.size.width),
int(videoProfile.size.height) };
1049 config.videoSourceType = AVRECORDER_SURFACE_YUV;
1050 config.profile.videoBitrate = settings.videoBitRate() > 0
1051 ? settings.videoBitRate() : qualityToVideoBitrate(settings.quality(), videoSize);
1052 config.profile.videoCodec = videoCodecToOhos(settings.videoCodec());
1053 config.profile.videoFrameWidth = videoSize.width();
1054 config.profile.videoFrameHeight = videoSize.height();
1055 config.profile.videoFrameRate = settings.videoFrameRate() > 0
1056 ?
int(settings.videoFrameRate()) : 30;
1058 config.profile.isHdr =
false;
1059 config.profile.enableTemporalScale =
false;
1060 config.url =
const_cast<
char *>(urlBytes.constData());
1061 config.fileGenerationMode = AVRECORDER_APP_CREATE;
1062 config.maxDuration = 0;
1064 if (OH_AVRecorder_Prepare(m_recorder, &config) != AV_ERR_OK) {
1065 emit recorderErrorOccurred(
int(QMediaRecorder::FormatError),
1066 tr(
"OH_AVRecorder_Prepare failed"));
1073 if (OH_AVRecorder_GetInputSurface(m_recorder, &m_recorderWindow) != AV_ERR_OK
1074 || !m_recorderWindow) {
1075 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1076 tr(
"OH_AVRecorder_GetInputSurface failed"));
1081 uint64_t surfaceIdNum = 0;
1082 if (OH_NativeWindow_GetSurfaceId(m_recorderWindow, &surfaceIdNum) != 0
1083 || surfaceIdNum == 0) {
1084 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1085 tr(
"Failed to obtain recorder surface ID"));
1089 const QByteArray surfaceId = QByteArray::number(qulonglong(surfaceIdNum));
1091 if (!attachVideoOutput(videoProfile, surfaceId)) {
1092 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1093 tr(
"Failed to attach video output to capture session"));
1099 if (OH_AVRecorder_Start(m_recorder) != AV_ERR_OK) {
1100 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1101 tr(
"OH_AVRecorder_Start failed"));
1102 detachVideoOutput();
1107 m_recorderActualLocation = QUrl::fromLocalFile(resolved);
1108 emit recorderActualLocationChanged(m_recorderActualLocation);
1109 m_recorderPausedMs = 0;
1110 m_recorderResumeStartMs = 0;
1111 m_recorderTimer.restart();
1117 if (!m_recorder || m_recorderState != QMediaRecorder::RecordingState)
1119 if (OH_AVRecorder_Pause(m_recorder) == AV_ERR_OK) {
1120 m_recorderPausedMs += (m_recorderTimer.elapsed() - m_recorderResumeStartMs);
1126 if (!m_recorder || m_recorderState != QMediaRecorder::PausedState)
1128 if (OH_AVRecorder_Resume(m_recorder) == AV_ERR_OK)
1129 m_recorderResumeStartMs = m_recorderTimer.elapsed();
1136 OH_AVRecorder_Stop(m_recorder);
1137 detachVideoOutput();
1144 OH_AVRecorder_Release(m_recorder);
1145 m_recorder =
nullptr;
1147 m_recorderWindow =
nullptr;
1148 if (m_recorderState != QMediaRecorder::StoppedState) {
1149 m_recorderState = QMediaRecorder::StoppedState;
1150 emit recorderStateChanged(
int(QMediaRecorder::StoppedState));
1152 m_recorderTimer.invalidate();
1153 m_recorderPausedMs = 0;
1154 m_recorderResumeStartMs = 0;
1159#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()