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>(sink,
this);
214 connect(m_videoOutput.get(), &QOhosVideoOutput::surfaceReady,
this,
215 &QOhosCameraSession::onSurfaceReady);
220 if (m_active == active)
223 if (!startSession()) {
224 m_pendingStart =
true;
228 m_pendingStart =
false;
232 emit activeChanged(active);
233 emitReadyForCaptureChanged();
238 m_imageSettings = settings;
243 const int id = ++m_lastCaptureId;
244 if (!m_active || !m_photoOutput) {
245 emit imageCaptureError(id, QImageCapture::NotReadyError,
246 tr(
"Camera not ready for capture"));
249 if (m_captureInProgress) {
250 emit imageCaptureError(id, QImageCapture::NotReadyError,
251 tr(
"Capture already in progress"));
255 m_pendingCaptureId = id;
256 m_pendingCaptureFileName = fileName;
257 m_pendingCaptureToBuffer = toBuffer;
258 m_captureInProgress =
true;
259 emitReadyForCaptureChanged();
261 if (OH_PhotoOutput_Capture(m_photoOutput) != CAMERA_OK) {
262 m_captureInProgress =
false;
263 emit imageCaptureError(id, QImageCapture::ResourceError,
264 tr(
"OH_PhotoOutput_Capture failed"));
265 emitReadyForCaptureChanged();
272 if (m_pendingStart && !m_active) {
273 m_pendingStart =
false;
274 if (startSession()) {
276 emit activeChanged(
true);
277 emitReadyForCaptureChanged();
284 if (m_active && !m_previewOutput && m_videoOutput
285 && !m_videoOutput->surfaceId().isEmpty()) {
288 if (startSession()) {
290 emit activeChanged(
true);
291 emitReadyForCaptureChanged();
298 if (!m_imageReceiver)
301 OH_ImageNative *image =
nullptr;
302 if (OH_ImageReceiverNative_ReadLatestImage(m_imageReceiver, &image) != IMAGE_SUCCESS
304 m_captureInProgress =
false;
305 emit imageCaptureError(m_pendingCaptureId, QImageCapture::ResourceError,
306 tr(
"Failed to read captured image"));
307 emitReadyForCaptureChanged();
311 const int quality = qualityToInt(m_imageSettings.quality());
312 QByteArray jpegBytes = encodeNativeImageToJpeg(image, quality);
313 OH_ImageNative_Release(image);
315 const int id = m_pendingCaptureId;
316 const QString fileName = m_pendingCaptureFileName;
317 const bool toBuffer = m_pendingCaptureToBuffer;
318 m_pendingCaptureFileName.clear();
319 m_pendingCaptureToBuffer =
false;
320 m_pendingCaptureId = 0;
321 m_captureInProgress =
false;
323 if (jpegBytes.isEmpty()) {
324 emit imageCaptureError(id, QImageCapture::FormatError,
325 tr(
"Failed to encode captured image"));
326 emitReadyForCaptureChanged();
330 QImage preview = QImage::fromData(jpegBytes,
"JPEG");
331 emit imageExposed(id);
332 emit imageCaptured(id, preview);
335 QVideoFrame buffer = QVideoFramePrivate::createFrame(
336 std::make_unique<QMemoryVideoBuffer>(QByteArray(jpegBytes),
337 preview.bytesPerLine()),
338 QVideoFrameFormat(preview.size(), QVideoFrameFormat::Format_Jpeg));
339 emit imageAvailable(id, buffer);
340 emitReadyForCaptureChanged();
347 const QImageCapture::FileFormat targetFormat = m_imageSettings.format();
348 const QString defaultExt = [&]() -> QString {
349 switch (targetFormat) {
350 case QImageCapture::PNG:
return QStringLiteral(
"png");
351 case QImageCapture::WebP:
return QStringLiteral(
"webp");
352 case QImageCapture::Tiff:
return QStringLiteral(
"tiff");
353 case QImageCapture::JPEG:
354 case QImageCapture::UnspecifiedFormat:
355 default:
return QStringLiteral(
"jpg");
358 const char *qImageFormat = [&]() ->
const char * {
359 switch (targetFormat) {
360 case QImageCapture::PNG:
return "PNG";
361 case QImageCapture::WebP:
return "WEBP";
362 case QImageCapture::Tiff:
return "TIFF";
363 default:
return nullptr;
366 const QString resolved = QMediaStorageLocation::generateFileName(
367 fileName, QStandardPaths::PicturesLocation, defaultExt);
370 saved = preview.save(resolved, qImageFormat, quality);
373 if (out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
374 out.write(jpegBytes);
380 emit imageSaved(id, resolved);
382 emit imageCaptureError(id, QImageCapture::ResourceError,
383 tr(
"Could not save captured image to: %1").arg(resolved));
386 emitReadyForCaptureChanged();
392 if (m_lastReadyForCapture && *m_lastReadyForCapture == ready)
394 m_lastReadyForCapture = ready;
395 emit readyForCaptureChanged(ready);
402 if (OH_Camera_GetCameraManager(&m_manager) != CAMERA_OK || !m_manager) {
403 qCWarning(qLcOhosMediaPlugin) <<
"OH_Camera_GetCameraManager failed";
406 if (OH_CameraManager_GetSupportedCameras(m_manager, &m_supportedDevices,
407 &m_supportedDeviceCount) != CAMERA_OK) {
408 qCWarning(qLcOhosMediaPlugin) <<
"GetSupportedCameras failed";
409 m_supportedDevices =
nullptr;
410 m_supportedDeviceCount = 0;
417 if (!m_supportedDevices)
419 for (uint32_t i = 0; i < m_supportedDeviceCount; ++i) {
420 if (m_supportedDevices[i].cameraId && id == m_supportedDevices[i].cameraId)
421 return &m_supportedDevices[i];
427 Camera_Profile *previewProfile)
429 if (!caps || caps->photoProfilesSize == 0)
432 Camera_Profile *photoProfile = caps->photoProfiles[0];
433 if (m_imageSettings.resolution().isValid()) {
434 const QSize wanted = m_imageSettings.resolution();
435 for (uint32_t i = 0; i < caps->photoProfilesSize; ++i) {
436 Camera_Profile *p = caps->photoProfiles[i];
439 if (
int(p->size.width) == wanted.width()
440 &&
int(p->size.height) == wanted.height()) {
445 }
else if (previewProfile) {
446 for (uint32_t i = 0; i < caps->photoProfilesSize; ++i) {
447 Camera_Profile *p = caps->photoProfiles[i];
450 if (p->size.width == previewProfile->size.width
451 && p->size.height == previewProfile->size.height) {
460 if (OH_ImageReceiverOptions_Create(&m_imageReceiverOptions) != IMAGE_SUCCESS
461 || !m_imageReceiverOptions) {
464 Image_Size size{ uint32_t(photoProfile->size.width), uint32_t(photoProfile->size.height) };
465 OH_ImageReceiverOptions_SetSize(m_imageReceiverOptions, size);
468 if (OH_ImageReceiverNative_Create(m_imageReceiverOptions, &m_imageReceiver) != IMAGE_SUCCESS
469 || !m_imageReceiver) {
473 OH_ImageReceiverNative_OnImageArrive(m_imageReceiver, imageArriveCallbackTrampoline,
this);
475 uint64_t surfaceIdNum = 0;
476 if (OH_ImageReceiverNative_GetReceivingSurfaceId(m_imageReceiver, &surfaceIdNum) != IMAGE_SUCCESS
477 || surfaceIdNum == 0) {
480 const QByteArray surfaceId = QByteArray::number(qulonglong(surfaceIdNum));
482 if (OH_CameraManager_CreatePhotoOutput(m_manager, photoProfile, surfaceId.constData(),
483 &m_photoOutput) != CAMERA_OK
493 OH_PhotoOutput_Release(m_photoOutput);
494 m_photoOutput =
nullptr;
496 if (m_imageReceiver) {
497 OH_ImageReceiverNative_OffImageArrive(m_imageReceiver, imageArriveCallbackTrampoline);
498 OH_ImageReceiverNative_Release(m_imageReceiver);
499 m_imageReceiver =
nullptr;
501 if (m_imageReceiverOptions) {
502 OH_ImageReceiverOptions_Release(m_imageReceiverOptions);
503 m_imageReceiverOptions =
nullptr;
513 QMutexLocker lock{ &liveSessionMutex() };
514 const auto sessions = liveSessions();
515 for (
auto *other : sessions) {
517 other->stopSession();
525 if (!m_videoOutput) {
526 m_videoOutput = std::make_unique<QOhosVideoOutput>(
nullptr,
this);
527 connect(m_videoOutput.get(), &QOhosVideoOutput::surfaceReady,
this,
528 &QOhosCameraSession::onSurfaceReady);
530 QByteArray previewSurfaceId = m_videoOutput->surfaceId();
532 if (!ensureManager())
535 Camera_Device *device = findDevice(m_cameraDevice.id());
536 if (!device && m_supportedDeviceCount > 0)
537 device = &m_supportedDevices[0];
539 qCWarning(qLcOhosMediaPlugin) <<
"No camera device available";
543 Camera_Profile *previewProfile =
nullptr;
544 Camera_OutputCapability *caps =
nullptr;
545 if (OH_CameraManager_GetSupportedCameraOutputCapability(m_manager, device, &caps) == CAMERA_OK
546 && caps && caps->previewProfilesSize > 0) {
547 previewProfile = caps->previewProfiles[0];
548 const bool haveRequest = !m_cameraFormat.isNull();
549 const QSize requestedSize = haveRequest ? m_cameraFormat.resolution() : QSize{};
550 const QVideoFrameFormat::PixelFormat requestedPixel =
551 haveRequest ? m_cameraFormat.pixelFormat() : QVideoFrameFormat::Format_Invalid;
552 for (uint32_t i = 0; i < caps->previewProfilesSize; ++i) {
553 Camera_Profile *p = caps->previewProfiles[i];
556 const bool sizeMatches = !haveRequest
557 || (
int(p->size.width) == requestedSize.width()
558 &&
int(p->size.height) == requestedSize.height());
559 const bool formatMatches = !haveRequest
560 || requestedPixel == QVideoFrameFormat::Format_Invalid
561 || requestedPixel == pixelFormatFor(p->format);
562 if (sizeMatches && formatMatches) {
566 if (p->format == CAMERA_FORMAT_YUV_420_SP && p->size.width == 1280
567 && p->size.height == 720)
573 if (!previewProfile) {
574 qCWarning(qLcOhosMediaPlugin) <<
"No preview profile available";
576 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
580 if (OH_CameraManager_CreateCameraInput(m_manager, device, &m_cameraInput) != CAMERA_OK
582 qCWarning(qLcOhosMediaPlugin) <<
"CreateCameraInput failed";
583 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
591 Camera_ErrorCode err = CAMERA_OK;
592 constexpr int kMaxAttempts = 20;
593 constexpr int kBackoffMs = 50;
594 for (
int attempt = 0; attempt < kMaxAttempts; ++attempt) {
595 err = OH_CameraInput_Open(m_cameraInput);
596 if (err == CAMERA_OK)
601 if (err == CAMERA_OPERATION_NOT_ALLOWED) {
602 QSet<QOhosCameraSession *> snapshot;
604 QMutexLocker lock{ &liveSessionMutex() };
605 snapshot = liveSessions();
607 for (
auto *other : snapshot) {
609 other->stopSession();
611 }
else if (err != CAMERA_CONFLICT_CAMERA && err != CAMERA_DEVICE_PREEMPTED) {
614 QThread::msleep(kBackoffMs);
616 if (err != CAMERA_OK) {
617 qCWarning(qLcOhosMediaPlugin) <<
"CameraInput_Open failed:" << err;
618 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
622 QMutexLocker lock{ &liveSessionMutex() };
626 if (m_videoOutput && !previewSurfaceId.isEmpty()) {
627 m_videoOutput->setVideoSize(
628 QSize{
int(previewProfile->size.width),
int(previewProfile->size.height) });
629 if (OH_CameraManager_CreatePreviewOutput(m_manager, previewProfile,
630 previewSurfaceId.constData(), &m_previewOutput)
632 || !m_previewOutput) {
633 qCWarning(qLcOhosMediaPlugin) <<
"CreatePreviewOutput failed";
634 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
640 const bool hasPhoto = createPhotoPath(caps, previewProfile);
642 qCWarning(qLcOhosMediaPlugin) <<
"Photo output unavailable; image capture disabled";
644 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
646 if (!m_previewOutput && !m_photoOutput) {
647 qCWarning(qLcOhosMediaPlugin) <<
"No capture outputs available";
652 if (OH_CameraManager_CreateCaptureSession(m_manager, &m_captureSession) != CAMERA_OK
653 || !m_captureSession) {
654 qCWarning(qLcOhosMediaPlugin) <<
"CreateCaptureSession failed";
659 OH_CaptureSession_BeginConfig(m_captureSession);
660 OH_CaptureSession_AddInput(m_captureSession, m_cameraInput);
662 OH_CaptureSession_AddPreviewOutput(m_captureSession, m_previewOutput);
664 OH_CaptureSession_AddPhotoOutput(m_captureSession, m_photoOutput);
665 if (OH_CaptureSession_CommitConfig(m_captureSession) != CAMERA_OK) {
666 qCWarning(qLcOhosMediaPlugin) <<
"CommitConfig failed";
671 if (OH_CaptureSession_Start(m_captureSession) != CAMERA_OK) {
672 qCWarning(qLcOhosMediaPlugin) <<
"CaptureSession_Start failed";
682 if (m_captureSession) {
683 OH_CaptureSession_Stop(m_captureSession);
693 if (m_captureSession) {
694 OH_CaptureSession_Release(m_captureSession);
695 m_captureSession =
nullptr;
697 if (m_previewOutput) {
698 OH_PreviewOutput_Release(m_previewOutput);
699 m_previewOutput =
nullptr;
702 OH_CameraInput_Close(m_cameraInput);
703 OH_CameraInput_Release(m_cameraInput);
704 m_cameraInput =
nullptr;
706 QMutexLocker lock{ &liveSessionMutex() };
712OH_AVRecorder_CodecMimeType videoCodecToOhos(QMediaFormat::VideoCodec codec)
715 case QMediaFormat::VideoCodec::H265:
716 return AVRECORDER_VIDEO_HEVC;
717 case QMediaFormat::VideoCodec::MPEG4:
718 return AVRECORDER_VIDEO_MPEG4;
719 case QMediaFormat::VideoCodec::H264:
720 case QMediaFormat::VideoCodec::Unspecified:
722 return AVRECORDER_VIDEO_AVC;
726OH_AVRecorder_CodecMimeType audioCodecToOhos(QMediaFormat::AudioCodec codec)
729 case QMediaFormat::AudioCodec::MP3:
730 return AVRECORDER_AUDIO_MP3;
731 case QMediaFormat::AudioCodec::AAC:
732 case QMediaFormat::AudioCodec::Unspecified:
734 return AVRECORDER_AUDIO_AAC;
738OH_AVRecorder_ContainerFormatType containerToOhos(QMediaFormat::FileFormat fmt)
741 case QMediaFormat::AAC:
742 return AVRECORDER_CFT_AAC;
743 case QMediaFormat::MP3:
744 return AVRECORDER_CFT_MP3;
745 case QMediaFormat::Wave:
746 return AVRECORDER_CFT_WAV;
747 case QMediaFormat::Mpeg4Audio:
748 return AVRECORDER_CFT_MPEG_4A;
749 case QMediaFormat::MPEG4:
750 case QMediaFormat::UnspecifiedFormat:
752 return AVRECORDER_CFT_MPEG_4;
756int qualityToVideoBitrate(QMediaRecorder::Quality q,
const QSize &resolution)
758 const int pixels = qMax(1, resolution.width() * resolution.height());
759 const double bpp = [&]() {
761 case QMediaRecorder::VeryLowQuality:
return 0.05;
762 case QMediaRecorder::LowQuality:
return 0.1;
763 case QMediaRecorder::HighQuality:
return 0.25;
764 case QMediaRecorder::VeryHighQuality:
return 0.4;
765 case QMediaRecorder::NormalQuality:
766 default:
return 0.15;
769 return int(pixels * 30 * bpp);
772int qualityToAudioBitrate(QMediaRecorder::Quality q)
775 case QMediaRecorder::VeryLowQuality:
return 32000;
776 case QMediaRecorder::LowQuality:
return 64000;
777 case QMediaRecorder::HighQuality:
return 192000;
778 case QMediaRecorder::VeryHighQuality:
return 256000;
779 case QMediaRecorder::NormalQuality:
780 default:
return 128000;
787 Camera_VideoProfile *out)
789 if (!m_manager || !out)
791 Camera_Device *device = findDevice(m_cameraDevice.id());
792 if (!device && m_supportedDeviceCount > 0)
793 device = &m_supportedDevices[0];
797 Camera_OutputCapability *caps =
nullptr;
798 if (OH_CameraManager_GetSupportedCameraOutputCapability(m_manager, device, &caps) != CAMERA_OK
799 || !caps || caps->videoProfilesSize == 0) {
801 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
805 Camera_VideoProfile *chosen =
nullptr;
806 const QSize wanted = settings.videoResolution();
807 if (wanted.isValid()) {
808 for (uint32_t i = 0; i < caps->videoProfilesSize; ++i) {
809 Camera_VideoProfile *p = caps->videoProfiles[i];
812 if (
int(p->size.width) == wanted.width()
813 &&
int(p->size.height) == wanted.height()) {
820 for (uint32_t i = 0; i < caps->videoProfilesSize; ++i) {
821 Camera_VideoProfile *p = caps->videoProfiles[i];
824 if (p->size.width == 1280 && p->size.height == 720
825 && p->format == CAMERA_FORMAT_YUV_420_SP) {
832 chosen = caps->videoProfiles[0];
834 OH_CameraManager_DeleteSupportedCameraOutputCapability(m_manager, caps);
839 const QByteArray &surfaceId)
841 if (!m_captureSession)
843 if (OH_CameraManager_CreateVideoOutput(m_manager, &profile, surfaceId.constData(),
844 &m_videoOutputCamera) != CAMERA_OK
845 || !m_videoOutputCamera) {
849 OH_CaptureSession_Stop(m_captureSession);
850 OH_CaptureSession_BeginConfig(m_captureSession);
851 OH_CaptureSession_AddVideoOutput(m_captureSession, m_videoOutputCamera);
852 if (OH_CaptureSession_CommitConfig(m_captureSession) != CAMERA_OK) {
853 OH_VideoOutput_Release(m_videoOutputCamera);
854 m_videoOutputCamera =
nullptr;
855 OH_CaptureSession_Start(m_captureSession);
858 if (OH_CaptureSession_Start(m_captureSession) != CAMERA_OK)
860 if (OH_VideoOutput_Start(m_videoOutputCamera) != CAMERA_OK)
867 if (!m_videoOutputCamera)
869 OH_VideoOutput_Stop(m_videoOutputCamera);
870 if (m_captureSession) {
871 OH_CaptureSession_Stop(m_captureSession);
872 OH_CaptureSession_BeginConfig(m_captureSession);
873 OH_CaptureSession_RemoveVideoOutput(m_captureSession, m_videoOutputCamera);
874 OH_CaptureSession_CommitConfig(m_captureSession);
875 OH_CaptureSession_Start(m_captureSession);
877 OH_VideoOutput_Release(m_videoOutputCamera);
878 m_videoOutputCamera =
nullptr;
882 OH_AVRecorder_State state,
883 OH_AVRecorder_StateChangeReason ,
889 QMetaObject::invokeMethod(self,
"onRecorderStateNotification", Qt::QueuedConnection,
890 Q_ARG(
int,
int(state)));
894 const char *errorMsg,
void *userData)
899 QMetaObject::invokeMethod(self,
"onRecorderErrorNotification", Qt::QueuedConnection,
900 Q_ARG(
int, errorCode),
901 Q_ARG(QString, QString::fromUtf8(errorMsg ? errorMsg :
"")));
906 QMediaRecorder::RecorderState mapped = m_recorderState;
908 case AVRECORDER_STARTED:
909 mapped = QMediaRecorder::RecordingState;
911 case AVRECORDER_PAUSED:
912 mapped = QMediaRecorder::PausedState;
914 case AVRECORDER_STOPPED:
915 case AVRECORDER_IDLE:
916 case AVRECORDER_RELEASED:
917 mapped = QMediaRecorder::StoppedState;
919 case AVRECORDER_ERROR:
920 mapped = QMediaRecorder::StoppedState;
925 if (mapped == m_recorderState)
927 m_recorderState = mapped;
928 emit recorderStateChanged(
int(mapped));
933 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
935 ? tr(
"Recorder error %1").arg(code)
941 if (m_recorderState == QMediaRecorder::StoppedState)
943 if (m_recorderState == QMediaRecorder::PausedState)
944 return m_recorderPausedMs;
945 if (!m_recorderTimer.isValid())
946 return m_recorderPausedMs;
947 return m_recorderPausedMs + (m_recorderTimer.elapsed() - m_recorderResumeStartMs);
951 const QString &location)
954 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
955 tr(
"Recording already in progress"));
961 const bool videoEnabled = m_active && m_captureSession;
962 Camera_VideoProfile videoProfile{};
964 if (!findVideoProfile(settings, &videoProfile)) {
965 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
966 tr(
"No matching camera video profile"));
971 m_recorder = OH_AVRecorder_Create();
973 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
974 tr(
"OH_AVRecorder_Create failed"));
978 OH_AVRecorder_SetStateCallback(m_recorder, recorderStateCallback,
this);
979 OH_AVRecorder_SetErrorCallback(m_recorder, recorderErrorCallback,
this);
981 QString resolved = location;
982 if (QFileInfo(resolved).suffix().isEmpty()) {
983 const QString suffix = settings.preferredSuffix();
984 if (!suffix.isEmpty())
985 resolved.append(QLatin1Char(
'.')).append(suffix);
987 resolved.append(QStringLiteral(
".mp4"));
990 QByteArray urlBytes = QStringLiteral(
"fd://").toUtf8();
991 int fd = ::open(QFile::encodeName(resolved).constData(),
992 O_RDWR | O_CREAT | O_TRUNC, 0644);
994 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
995 tr(
"Could not open output file: %1").arg(resolved));
999 urlBytes.append(QByteArray::number(fd));
1001 OH_AVRecorder_Config config{};
1002 config.audioSourceType = AVRECORDER_MIC;
1003 config.profile.audioBitrate = settings.audioBitRate() > 0
1004 ? settings.audioBitRate() : qualityToAudioBitrate(settings.quality());
1005 config.profile.audioChannels = settings.audioChannelCount() > 0
1006 ? settings.audioChannelCount() : 2;
1007 config.profile.audioCodec = audioCodecToOhos(settings.audioCodec());
1008 config.profile.audioSampleRate = settings.audioSampleRate() > 0
1009 ? settings.audioSampleRate() : 48000;
1010 config.profile.fileFormat = containerToOhos(settings.fileFormat());
1012 const QSize videoSize{
int(videoProfile.size.width),
int(videoProfile.size.height) };
1013 config.videoSourceType = AVRECORDER_SURFACE_YUV;
1014 config.profile.videoBitrate = settings.videoBitRate() > 0
1015 ? settings.videoBitRate() : qualityToVideoBitrate(settings.quality(), videoSize);
1016 config.profile.videoCodec = videoCodecToOhos(settings.videoCodec());
1017 config.profile.videoFrameWidth = videoSize.width();
1018 config.profile.videoFrameHeight = videoSize.height();
1019 config.profile.videoFrameRate = settings.videoFrameRate() > 0
1020 ?
int(settings.videoFrameRate()) : 30;
1022 config.profile.isHdr =
false;
1023 config.profile.enableTemporalScale =
false;
1024 config.url =
const_cast<
char *>(urlBytes.constData());
1025 config.fileGenerationMode = AVRECORDER_APP_CREATE;
1026 config.maxDuration = 0;
1028 if (OH_AVRecorder_Prepare(m_recorder, &config) != AV_ERR_OK) {
1029 emit recorderErrorOccurred(
int(QMediaRecorder::FormatError),
1030 tr(
"OH_AVRecorder_Prepare failed"));
1037 if (OH_AVRecorder_GetInputSurface(m_recorder, &m_recorderWindow) != AV_ERR_OK
1038 || !m_recorderWindow) {
1039 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1040 tr(
"OH_AVRecorder_GetInputSurface failed"));
1045 uint64_t surfaceIdNum = 0;
1046 if (OH_NativeWindow_GetSurfaceId(m_recorderWindow, &surfaceIdNum) != 0
1047 || surfaceIdNum == 0) {
1048 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1049 tr(
"Failed to obtain recorder surface ID"));
1053 const QByteArray surfaceId = QByteArray::number(qulonglong(surfaceIdNum));
1055 if (!attachVideoOutput(videoProfile, surfaceId)) {
1056 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1057 tr(
"Failed to attach video output to capture session"));
1063 if (OH_AVRecorder_Start(m_recorder) != AV_ERR_OK) {
1064 emit recorderErrorOccurred(
int(QMediaRecorder::ResourceError),
1065 tr(
"OH_AVRecorder_Start failed"));
1066 detachVideoOutput();
1071 m_recorderActualLocation = QUrl::fromLocalFile(resolved);
1072 emit recorderActualLocationChanged(m_recorderActualLocation);
1073 m_recorderPausedMs = 0;
1074 m_recorderResumeStartMs = 0;
1075 m_recorderTimer.restart();
1081 if (!m_recorder || m_recorderState != QMediaRecorder::RecordingState)
1083 if (OH_AVRecorder_Pause(m_recorder) == AV_ERR_OK) {
1084 m_recorderPausedMs += (m_recorderTimer.elapsed() - m_recorderResumeStartMs);
1090 if (!m_recorder || m_recorderState != QMediaRecorder::PausedState)
1092 if (OH_AVRecorder_Resume(m_recorder) == AV_ERR_OK)
1093 m_recorderResumeStartMs = m_recorderTimer.elapsed();
1100 OH_AVRecorder_Stop(m_recorder);
1101 detachVideoOutput();
1108 OH_AVRecorder_Release(m_recorder);
1109 m_recorder =
nullptr;
1111 m_recorderWindow =
nullptr;
1112 if (m_recorderState != QMediaRecorder::StoppedState) {
1113 m_recorderState = QMediaRecorder::StoppedState;
1114 emit recorderStateChanged(
int(QMediaRecorder::StoppedState));
1116 m_recorderTimer.invalidate();
1117 m_recorderPausedMs = 0;
1118 m_recorderResumeStartMs = 0;
1123#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()