@@ -54,6 +54,9 @@ class QCameraViewfinderSettingsPrivate : public QSharedData
public:
QCameraViewfinderSettingsPrivate() :
isNull(true),
+#ifdef Q_OS_OPENHARMONY
+ isMirrored(false),
+#endif
minimumFrameRate(0.0),
maximumFrameRate(0.0),
pixelFormat(QVideoFrame::Format_Invalid)
@@ -63,6 +66,9 @@ public:
QCameraViewfinderSettingsPrivate(const QCameraViewfinderSettingsPrivate &other):
QSharedData(other),
isNull(other.isNull),
+#ifdef Q_OS_OPENHARMONY
+ isMirrored(other.isMirrored),
+#endif
resolution(other.resolution),
minimumFrameRate(other.minimumFrameRate),
maximumFrameRate(other.maximumFrameRate),
@@ -72,6 +78,9 @@ public:
}
bool isNull;
+#ifdef Q_OS_OPENHARMONY
+ bool isMirrored;
+#endif
QSize resolution;
qreal minimumFrameRate;
qreal maximumFrameRate;
@@ -165,6 +174,9 @@ bool operator==(const QCameraViewfinderSettings &lhs, const QCameraViewfinderSet
{
return (lhs.d == rhs.d) ||
(lhs.d->isNull == rhs.d->isNull &&
+#ifdef Q_OS_OPENHARMONY
+ lhs.d->isMirrored == rhs.d->isMirrored &&
+#endif
lhs.d->resolution == rhs.d->resolution &&
lhs.d->minimumFrameRate == rhs.d->minimumFrameRate &&
lhs.d->maximumFrameRate == rhs.d->maximumFrameRate &&
@@ -254,6 +266,18 @@ void QCameraViewfinderSettings::setMinimumFrameRate(qreal rate)
d->minimumFrameRate = rate;
}
+#ifdef Q_OS_OPENHARMONY
+bool QCameraViewfinderSettings::mirrored()
+{
+ return d->isMirrored;
+}
+
+void QCameraViewfinderSettings::setMirrored(bool mirrored)
+{
+ d->isMirrored = mirrored;
+}
+#endif
+
/*!
Returns the viewfinder maximum frame rate in frames per second.
@@ -77,6 +77,11 @@ public:
qreal minimumFrameRate() const;
void setMinimumFrameRate(qreal rate);
+#ifdef Q_OS_OPENHARMONY
+ bool mirrored();
+ void setMirrored(bool mirrored);
+#endif
+
qreal maximumFrameRate() const;
void setMaximumFrameRate(qreal rate);
@@ -354,10 +354,16 @@ void QMediaPlayerPrivate::setMedia(const QMediaContent &media, QIODevice *stream
// Backends can't play qrc files directly.
// If the backend supports StreamPlayback, we pass a QFile for that resource.
// If it doesn't, we copy the data to a temporary file and pass its path.
- if (!media.isNull() && !stream && media.request().url().scheme() == QLatin1String("qrc")) {
+ QString scheme = media.request().url().scheme();
+ if (!media.isNull() && !stream && (scheme == QLatin1String("qrc")
+ || scheme == QLatin1String("rawfile"))) {
qrcMedia = media;
- file.reset(new QFile(QLatin1Char(':') + media.request().url().path()));
+ if (scheme == QLatin1String("qrc")) {
+ file.reset(new QFile(QLatin1Char(':') + media.request().url().path()));
+ } else if (scheme == QLatin1String("rawfile")) {
+ file.reset(new QFile(media.request().url().toString()));
+ }
if (!file->open(QFile::ReadOnly)) {
QMetaObject::invokeMethod(q, "_q_error", Qt::QueuedConnection,
Q_ARG(int, QMediaPlayer::ResourceError),
@@ -142,6 +142,7 @@ bool QVideoSurfaceGenericPainter::isFormatSupported(const QVideoSurfaceFormat &f
QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::start(const QVideoSurfaceFormat &format)
{
m_frame = QVideoFrame();
+
m_imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
// Do not render into ARGB32 images using QPainter.
// Using QImage::Format_ARGB32_Premultiplied is significantly faster.
@@ -196,7 +197,6 @@ QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::paint(
m_imageSize.height(),
m_frame.bytesPerLine(),
m_imageFormat);
-
const QTransform oldTransform = painter->transform();
QTransform transform = oldTransform;
QRectF targetRect = target;
@@ -216,8 +216,6 @@ QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::paint(
painter->setTransform(oldTransform);
m_frame.unmap();
- } else if (m_frame.isValid()) {
- return QAbstractVideoSurface::IncorrectFormatError;
} else {
painter->fillRect(target, Qt::black);
}
@@ -630,9 +630,11 @@ QVideoWidget::QVideoWidget(QVideoWidgetPrivate &dd, QWidget *parent)
{
d_ptr->q_ptr = this;
+#ifndef Q_OS_OPENHARMONY
QPalette palette = QWidget::palette();
palette.setColor(QPalette::Window, Qt::black);
setPalette(palette);
+#endif
}
/*!
new file mode 100644
@@ -0,0 +1,101 @@
+import audio from '@ohos.multimedia.audio';
+import JsLogger from '../QtCore/JsLogger';
+
+
+export class JsAudioManager {
+ private audioManager: audio.AudioManager = audio.getAudioManager();
+ private routingManager: audio.AudioRoutingManager = this.audioManager.getRoutingManager();
+ private deviceDescript = new Map([
+ [0, "INVALID"], [1, "EARPIECE"], [2, "SPEAKER"], [3, "WIRED_HEADSET"], [4, "WIRED_HEADPHONES"],
+ [7, "BLUETOOTH_SCO"], [8, "BLUETOOTH_A2DP"], [15, "MIC"], [22, "USB_HEADSET"], [1000, "DEFAULT"]
+ ]);
+
+ public constructor() {
+ }
+
+ /* 获取可用的输入设备描述 */
+ private async availableInputDevicesDes() {
+ let devicesDes = await this.routingManager.getDevices(audio.DeviceFlag.INPUT_DEVICES_FLAG);
+ return devicesDes;
+ }
+
+ /* 获取可用的输出设备描述 */
+ private async availableOutputDevicesDes() {
+ let devices = await this.routingManager.getDevices(audio.DeviceFlag.OUTPUT_DEVICES_FLAG);
+ return devices;
+ }
+
+ /* 获取可用的输入设备id值 */
+ async availableInputDevices() {
+ let devices: string = '';//Array<string>作为返回值C++只能获取空值
+ let devDes = await this.availableInputDevicesDes();
+ for (let des of devDes) {
+ if (audio.DeviceType.INVALID != des.deviceType) {
+ devices += des.displayName + '|';
+ }
+ }
+
+ if(devices.length > 0)
+ return devices.slice(0, -1);
+ return devices;
+ }
+ /* 获取可用的输出设备id值 */
+ async availableOutputDevices() {
+ let devices: string = '';//Array<string>作为返回值C++只能获取空值
+ let devDes = await this.availableOutputDevicesDes();
+ for (let des of devDes) {
+ if (audio.DeviceType.INVALID != des.deviceType) {
+ devices += des.displayName + '|';
+ }
+ }
+
+ if(devices.length > 0)
+ return devices.slice(0, -1);
+ return devices;
+ }
+
+ /* 获取指定输入设备支持的通道数 param id---设备id */
+ async inputChannelCounts(name: string) {
+ let devices = await this.availableInputDevicesDes();
+ for (let dev of devices) {
+ if (name === dev.displayName) {
+ return dev.channelCounts.join(',');
+ }
+ }
+ return '';
+ }
+
+ /* 获取指定输出设备支持的通道数 param id---设备id */
+ async outputChannelCounts(name: string) {
+ let devices = await this.availableOutputDevicesDes();
+ for (let dev of devices) {
+ if (name === dev.displayName) {
+ return dev.channelCounts.join(',');
+ }
+ }
+ return '';
+ }
+
+ /* 返回指定输入设备支持的采样率列表 param id---设备id */
+ async inputSupportedSampleRates(name: string) {
+ JsLogger.info("call into inputSupportedSampleRates: %{public}s", name);
+ let devices = await this.availableInputDevicesDes();
+ for (let dev of devices) {
+ if (name === dev.displayName) {
+ JsLogger.info("return: %{public}s", dev.sampleRates.join(','));
+ return dev.sampleRates.join(',');
+ }
+ }
+ return '';
+ }
+ /* 返回输出设备支持的采样率列表 param id---设备id */
+ async outputSupportedSampleRates(name: string) {
+ let devices = await this.availableOutputDevicesDes();
+ for (let dev of devices) {
+ if (name === dev.displayName) {
+ return dev.sampleRates.join(',');
+ }
+ }
+ return '';
+ }
+}
new file mode 100644
@@ -0,0 +1,56 @@
+import camera from '@ohos.multimedia.camera';
+import JsDataStore from '../QtCore/JsDataStore';
+
+export class JsCameraManager {
+ private cameraDevs = new Map();
+
+ constructor() {
+ }
+
+ private interfaceToObject(data:camera.CameraDevice):any {
+ var cameraInfo = {
+ "cameraId" : data.cameraId,
+ "cameraType" : data.cameraType,
+ "cameraPosition" : data.cameraPosition,
+ "connectionType" : data.connectionType,
+ }
+ return cameraInfo;
+ }
+
+ /* 获取相机设备列表 Array<CameraDevice> */
+ private async cameraDevices() {
+ let cameraManager = await camera.getCameraManager(JsDataStore.getValidContext());
+ let cameras = await cameraManager.getSupportedCameras();
+ for (let dev of cameras) {
+ this.cameraDevs.set(dev.cameraId, dev);
+ }
+ return cameras;
+ }
+
+ /* 获取指定id的相机设备信息 */
+ cameraInfo(id: string): string {
+ if (this.cameraDevs.has(id)) {
+ let info = this.cameraDevs.get(id);
+ var cameraInfo = this.interfaceToObject(info);
+ return JSON.stringify(cameraInfo);
+ }
+ return null;
+ }
+
+ /* 获取相机设备数量 */
+ async idOfCameras() {
+ await this.cameraDevices();
+ return [...this.cameraDevs.keys()];
+ }
+
+ /* 获取相机设备信息的以JSON字符串格式表示 */
+ async jsonOfCameras() {
+ let cameras = await this.cameraDevices();
+ let devices = new Array();
+ for (let dev of cameras) {
+ var cameraInfo = this.interfaceToObject(dev);
+ devices.push(cameraInfo);
+ }
+ return JSON.stringify(devices);
+ }
+}
new file mode 100644
@@ -0,0 +1,90 @@
+import media from '@ohos.multimedia.media';
+import { to } from './JsMultimediaUtils';
+
+export class JsMediaRecorder {
+ private mRecorder: media.AVRecorder = null;
+
+ constructor() {
+ }
+
+ private async hasRecorder() {
+ if (null != this.mRecorder) {
+ return true;
+ }
+
+ let [error, record] = await to(media.createAVRecorder());
+ if (null != error) {
+ console.error(`createAVRecorder catchCallback, error:${error}`);
+ return false;
+ }
+ this.mRecorder = record;
+ return true;
+ }
+
+ async release() {
+ if (this.hasRecorder()) {
+ let [error, placeholder] = await to(this.mRecorder.release());
+ if (null != error) {
+ console.error('release AVRecorder failed and catch error is ' + error.message);
+ }
+ }
+ }
+
+ /* NOTE 配置项数据,C++端调用时传入json字符串 */
+ async prepare(configs:string) {
+ if (this.hasRecorder()) {
+ //TODO
+ var obj = JSON.parse(configs);
+ let AVRecorderProfile = {
+ audioBitrate : 48000,
+ audioChannels : 2,
+ audioCodec : media.CodecMimeType.AUDIO_AAC,
+ audioSampleRate : 48000,
+ fileFormat : media.ContainerFormatType.CFT_MPEG_4,
+ videoBitrate : 48000,
+ videoCodec : media.CodecMimeType.VIDEO_MPEG4,
+ videoFrameWidth : 640,
+ videoFrameHeight : 480,
+ videoFrameRate : 30
+ }
+ let AVRecorderConfig = {
+ audioSourceType : media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
+ videoSourceType : media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
+ profile : AVRecorderProfile,
+ url : 'fd://', // 文件需先由调用者创建,赋予读写权限,将文件fd传给此参数,eg.fd://45
+ rotation : 0, // 合理值0、90、180、270,非合理值prepare接口将报错
+ location : { latitude : 30, longitude : 130 }
+ }
+
+ this.mRecorder.prepare(AVRecorderConfig);
+ }
+ }
+
+ async reset() {
+ if (this.hasRecorder()) {
+ let [error, placeholder] = await to(this.mRecorder.reset());
+ if (null != error) {
+ console.error('reset AVRecorder failed and catch error is ' + error.message);
+ }
+ }
+ }
+
+ async start() {
+ if (this.hasRecorder()) {
+ let [error, placeholder] = await to(this.mRecorder.start());
+ if (null != error) {
+ console.info('start AVRecorder failed and catch error is ' + error.message);
+ }
+ }
+ }
+
+ async stop() {
+ if (this.hasRecorder()) {
+ let [error, placeholder] = await to(this.mRecorder.stop());
+ if (null != error) {
+ console.info('stop AVRecorder failed and error is ' + error.message);
+ }
+ }
+ }
+}
+
new file mode 100644
@@ -0,0 +1,34 @@
+import { JsQtModule, ObjectBuilder } from '../QtCore/JsQtModule';
+import JsDataStore from '../QtCore/JsDataStore';
+import { JsAudioManager } from './JsAudioManager'
+import { JsMediaRecorder } from './JsMediaRecorder'
+import { JsMultimediaUtils } from './JsMultimediaUtils'
+import { abilityAccessCtrl } from '@kit.AbilityKit';
+import { JsCameraManager } from './JsCameraManager'
+
+class JsMultiMediaModule extends JsQtModule {
+
+ public constructor() {
+ super()
+ this.moduleJsObjects.set("JsAudioManager", new ObjectBuilder<[]>(() =>{
+ return new JsAudioManager();
+ }));
+ this.moduleJsObjects.set("JsMediaRecorder", new ObjectBuilder<[]>(() =>{
+ return new JsMediaRecorder();
+ }));
+ this.moduleJsObjects.set("JsMultimediaUtils", new ObjectBuilder<[]>(() =>{
+ return new JsMultimediaUtils();
+ }));
+ this.moduleJsObjects.set("JsCameraManager", new ObjectBuilder<[]>(() =>{
+ return new JsCameraManager();
+ }));
+ }
+
+ async loadQtModule(): Promise<void> {
+ let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
+ atManager.requestPermissionsFromUser(JsDataStore.getValidContext(), ['ohos.permission.CAMERA',
+ 'ohos.permission.MICROPHONE']);
+ }
+}
+
+export default new JsMultiMediaModule;
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,21 @@
+import media from '@ohos.multimedia.media';
+// import mediaLibrary from '@ohos.multimedia.mediaLibrary';
+import JsDataStore from '../QtCore/JsDataStore';
+
+export class JsMultimediaUtils {
+ // private media = mediaLibrary.getMediaLibrary(JsDataStore.getValidContext());
+
+ constructor(){}
+
+ async getMediaDirectory(type:number){
+ // const dictResult = await this.media.getPublicDirectory(type);
+ // return dictResult;
+ return undefined;
+ }
+}
+
+export function to(promise) {
+ return promise.then(data => {
+ return [null, data];
+ }).catch(err => [err]);
+}
new file mode 100644
@@ -0,0 +1,10 @@
+TEMPLATE = aux
+
+CONFIG -= qt
+
+templates.files += $$files($$PWD/native/QtMultiMedia/*.ts, true)
+templates.files += $$files($$PWD/native/QtMultiMedia/*.ets, true)
+templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtmultimedia
+templates.base = $$PWD
+
+INSTALLS += templates
new file mode 100644
@@ -0,0 +1,3 @@
+{
+ "Keys": ["default"]
+}
new file mode 100644
@@ -0,0 +1,27 @@
+TARGET = qtaudio_ohaudiodevice
+QT += multimedia-private core-private
+
+LIBS += -lohaudio -lnative_media_codecbase
+
+HEADERS += \
+ qohaudiodeviceplugin.h \
+ qopenharmonyaudioinput.h \
+ qopenharmonyaudiomanager.h \
+ qopenharmonyaudiooutput.h \
+ qopenharmonydeviceinfo.h \
+ qopenharmonyengine.h
+
+SOURCES += \
+ qohaudiodeviceplugin.cpp \
+ qopenharmonyaudioinput.cpp \
+ qopenharmonyaudiomanager.cpp \
+ qopenharmonyaudiooutput.cpp \
+ qopenharmonydeviceinfo.cpp \
+ qopenharmonyengine.cpp
+
+OTHER_FILES += \
+ ohaudiodevice.json
+
+PLUGIN_TYPE = audio
+PLUGIN_CLASS_NAME = QOHAudioDevicePlugin
+load(qt_plugin)
new file mode 100644
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qohaudiodeviceplugin.h"
+
+#include "qopenharmonyengine.h"
+#include "qopenharmonydeviceinfo.h"
+#include "qopenharmonyaudioinput.h"
+#include "qopenharmonyaudiooutput.h"
+
+QT_BEGIN_NAMESPACE
+
+QOHAudioDevicePlugin::QOHAudioDevicePlugin(QObject *parent)
+ : QAudioSystemPlugin(parent)
+ , m_engine(QOpenharmonyEngine::instance())
+{
+}
+
+QByteArray QOHAudioDevicePlugin::defaultDevice(QAudio::Mode mode) const
+{
+ return m_engine->defaultDevice(mode);
+}
+
+QList<QByteArray> QOHAudioDevicePlugin::availableDevices(QAudio::Mode mode) const
+{
+ return m_engine->availableDevices(mode);
+}
+
+QAbstractAudioInput *QOHAudioDevicePlugin::createInput(const QByteArray &device)
+{
+ return new QOpenharmonyAudioInput(device);
+}
+
+QAbstractAudioOutput *QOHAudioDevicePlugin::createOutput(const QByteArray &device)
+{
+ return new QOpenharmonyAudioOutput(device);
+
+}
+
+QAbstractAudioDeviceInfo *QOHAudioDevicePlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+{
+ return new QOpenharmonyDeviceInfo(device, mode);
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPENSLESPLUGIN_H
+#define QOPENSLESPLUGIN_H
+
+#include <QtMultimedia/qaudiosystemplugin.h>
+#include <QtMultimedia/private/qaudiosystempluginext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenharmonyEngine;
+
+class QOHAudioDevicePlugin : public QAudioSystemPlugin, public QAudioSystemPluginExtension
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "ohaudiodevice.json")
+ Q_INTERFACES(QAudioSystemPluginExtension)
+
+public:
+ QOHAudioDevicePlugin(QObject *parent = 0);
+ ~QOHAudioDevicePlugin() {}
+
+ QByteArray defaultDevice(QAudio::Mode mode) const;
+ QList<QByteArray> availableDevices(QAudio::Mode mode) const;
+ QAbstractAudioInput *createInput(const QByteArray &device);
+ QAbstractAudioOutput *createOutput(const QByteArray &device);
+ QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode);
+
+private:
+ QOpenharmonyEngine *m_engine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENSLESPLUGIN_H
new file mode 100644
@@ -0,0 +1,384 @@
+#include "qopenharmonyaudioinput.h"
+
+#include <qbuffer.h>
+#include <private/qaudiohelpers_p.h>
+#include <qdebug.h>
+#include "qopenharmony.h"
+
+QT_BEGIN_NAMESPACE
+
+#define DEFAULT_PERIOD_TIME_MS 50
+#define MINIMUM_PERIOD_TIME_MS 5
+// 自定义写入数据函数
+static int32_t MyOnReadData(OH_AudioCapturer* capturer, void* userData, void* buffer, int32_t length)
+{
+ Q_UNUSED(capturer)
+ // Process buffer in main thread
+ QByteArray dataArray((char *)buffer, length);
+
+ QMetaObject::invokeMethod(reinterpret_cast<QOpenharmonyAudioInput*>(userData), "processBuffer",\
+ Q_ARG(QByteArray, dataArray));
+ return 0;
+}
+// 自定义异常回调函数
+static int32_t MyOnError(OH_AudioCapturer* capturer, void* userData, OH_AudioStream_Result error)
+{
+ Q_UNUSED(capturer)
+ Q_UNUSED(userData)
+ qWarning() << "Audio get some error :" << error;
+ // 根据error表示的音频异常信息,做出相应的处理
+ return 0;
+}
+
+QOpenharmonyAudioInput::QOpenharmonyAudioInput(const QByteArray &device)
+ : m_device(device)
+ , m_pullMode(true)
+ , m_processedBytes(0)
+ , m_audioSource(0)
+ , m_bufferIODevice(0)
+ , m_errorState(QAudio::NoError)
+ , m_deviceState(QAudio::StoppedState)
+ , m_lastNotifyTime(0)
+ , m_volume(1.0)
+ , m_intervalTime(1000)
+ ,m_builder(nullptr)
+ ,m_capturer(nullptr)
+{
+
+}
+
+QOpenharmonyAudioInput::~QOpenharmonyAudioInput()
+{
+}
+
+void QOpenharmonyAudioInput::start(QIODevice *device)
+{
+ if (m_deviceState != QAudio::StoppedState)
+ stopRecording();
+
+ if (!m_pullMode && m_bufferIODevice) {
+ m_bufferIODevice->close();
+ delete m_bufferIODevice;
+ m_bufferIODevice = 0;
+ }
+
+ m_pullMode = true;
+ m_audioSource = device;
+
+ if (startRecording()) {
+ m_deviceState = QAudio::ActiveState;
+ } else {
+ m_deviceState = QAudio::StoppedState;
+ Q_EMIT errorChanged(m_errorState);
+ }
+
+ Q_EMIT stateChanged(m_deviceState);
+}
+
+QIODevice *QOpenharmonyAudioInput::start()
+{
+ if (m_deviceState != QAudio::StoppedState)
+ stopRecording();
+
+ m_audioSource = 0;
+
+ if (!m_pullMode && m_bufferIODevice) {
+ m_bufferIODevice->close();
+ delete m_bufferIODevice;
+ }
+
+ m_pullMode = false;
+ m_pushBuffer.clear();
+ m_bufferIODevice = new QBuffer(&m_pushBuffer);
+ m_bufferIODevice->open(QIODevice::ReadOnly);
+
+ if (startRecording()) {
+ m_deviceState = QAudio::IdleState;
+ } else {
+ m_deviceState = QAudio::StoppedState;
+ Q_EMIT errorChanged(m_errorState);
+ }
+
+ Q_EMIT stateChanged(m_deviceState);
+ return m_bufferIODevice;
+}
+
+void QOpenharmonyAudioInput::stop()
+{
+ if (m_deviceState == QAudio::StoppedState)
+ return;
+
+ m_deviceState = QAudio::StoppedState;
+
+ stopRecording();
+
+ m_errorState = QAudio::NoError;
+ Q_EMIT stateChanged(m_deviceState);
+}
+
+void QOpenharmonyAudioInput::reset()
+{
+ stop();
+}
+
+void QOpenharmonyAudioInput::suspend()
+{
+ if (m_deviceState != QAudio::ActiveState) {
+ return;
+ }
+ m_deviceState = QAudio::SuspendedState;
+ emit stateChanged(m_deviceState);
+
+ OH_AudioCapturer_Pause(m_capturer);//暂停录制
+}
+
+void QOpenharmonyAudioInput::resume()
+{
+ if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) {
+ OH_AudioCapturer_Start(m_capturer);//开始录制
+
+ m_deviceState = QAudio::ActiveState;
+ emit stateChanged(m_deviceState);
+ }
+}
+
+int QOpenharmonyAudioInput::bytesReady() const
+{
+ if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::SuspendedState)
+ return m_bufferIODevice ? m_bufferIODevice->bytesAvailable() : 0;
+
+ return 0;
+}
+
+int QOpenharmonyAudioInput::periodSize() const
+{
+ return 0;
+}
+
+void QOpenharmonyAudioInput::setBufferSize(int value)
+{
+ Q_UNUSED(value)
+}
+
+int QOpenharmonyAudioInput::bufferSize() const
+{
+ return 0;
+}
+
+void QOpenharmonyAudioInput::setNotifyInterval(int milliSeconds)
+{
+ m_intervalTime = qMax(0, milliSeconds);
+}
+
+int QOpenharmonyAudioInput::notifyInterval() const
+{
+ return m_intervalTime;
+}
+
+qint64 QOpenharmonyAudioInput::processedUSecs() const
+{
+ return m_format.durationForBytes(m_processedBytes);
+}
+
+qint64 QOpenharmonyAudioInput::elapsedUSecs() const
+{
+ if (m_deviceState == QAudio::StoppedState)
+ return 0;
+
+ return m_clockStamp.elapsed() * qint64(1000);
+}
+
+QAudio::Error QOpenharmonyAudioInput::error() const
+{
+ return m_errorState;
+}
+
+QAudio::State QOpenharmonyAudioInput::state() const
+{
+ return m_deviceState;
+}
+
+void QOpenharmonyAudioInput::setFormat(const QAudioFormat &format)
+{
+ if (m_deviceState == QAudio::StoppedState)
+ m_format = format;
+}
+
+QAudioFormat QOpenharmonyAudioInput::format() const
+{
+ return m_format;
+}
+
+void QOpenharmonyAudioInput::setVolume(qreal volume)
+{
+ m_volume = volume;
+}
+
+qreal QOpenharmonyAudioInput::volume() const
+{
+ return m_volume;
+}
+
+void QOpenharmonyAudioInput::processBuffer(const QByteArray& audioData)
+{
+ if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState)
+ return;
+
+ if (m_deviceState != QAudio::ActiveState) {
+ m_errorState = QAudio::NoError;
+ m_deviceState = QAudio::ActiveState;
+ emit stateChanged(m_deviceState);
+ }
+
+ //write Data To Device
+ int size = audioData.size();
+ m_processedBytes += size;
+
+ QByteArray outData;
+
+ if (m_volume < 1.0f) {// Apply volume
+ outData.resize(size);
+ QAudioHelperInternal::qMultiplySamples(m_volume, m_format, audioData.data(), outData.data(), size);
+ } else {
+ outData.append(audioData);
+ }
+
+ if (m_pullMode) {
+ // write buffer to the QIODevice
+ if (m_audioSource->write(outData) < 0) {
+ stop();
+ m_errorState = QAudio::IOError;
+ Q_EMIT errorChanged(m_errorState);
+ }
+ } else {
+ // emits readyRead() so user will call read() on QIODevice to get some audio data
+ if (m_bufferIODevice != 0) {
+ m_pushBuffer.append(outData);
+ Q_EMIT m_bufferIODevice->readyRead();
+ }
+ }
+
+ // Send notify signal if needed
+ qint64 processedMsecs = processedUSecs() / 1000;
+ if (m_intervalTime && (processedMsecs - m_lastNotifyTime) >= m_intervalTime) {
+ Q_EMIT notify();
+ m_lastNotifyTime = processedMsecs;
+ }
+}
+
+bool QOpenharmonyAudioInput::startRecording()
+{
+ m_processedBytes = 0;
+ m_clockStamp.restart();
+ m_lastNotifyTime = 0;
+
+ if(m_builder || m_capturer)
+ {
+ qWarning() << "Please stop Recording first!";
+ stopRecording();
+ }
+
+ OH_AudioStream_Type streamType = AUDIOSTREAM_TYPE_CAPTURER;
+ OH_AudioStream_Result ret = OH_AudioStreamBuilder_Create(&m_builder, streamType);
+ if(ret != AUDIOSTREAM_SUCCESS || !m_builder)
+ {
+ qWarning() << "Create Audio Stream Builder failed!";
+ m_errorState = QAudio::FatalError;
+ return false;
+ }
+
+ //设置低时延模式
+ OH_AudioStream_LatencyMode latencyMode = AUDIOSTREAM_LATENCY_MODE_FAST;
+ OH_AudioStreamBuilder_SetLatencyMode(m_builder, latencyMode);
+
+ // 设置音频采样率
+ OH_AudioStreamBuilder_SetSamplingRate(m_builder, m_format.sampleRate());
+ // 设置音频声道
+ OH_AudioStreamBuilder_SetChannelCount(m_builder, m_format.channelCount());
+ // 设置音频采样格式
+ OH_AudioStreamBuilder_SetSampleFormat(m_builder, sampleSizeToFormatEnum(m_format.sampleSize(), m_format.sampleType()));
+ // 设置音频流的编码类型
+ OH_AudioStreamBuilder_SetEncodingType(m_builder, AUDIOSTREAM_ENCODING_TYPE_RAW);
+ // 设置输入音频流的工作场景
+ OH_AudioStreamBuilder_SetCapturerInfo(m_builder, AUDIOSTREAM_SOURCE_TYPE_MIC);
+
+ OH_AudioCapturer_Callbacks callbacks;
+ // 配置回调函数
+ callbacks.OH_AudioCapturer_OnReadData = MyOnReadData;
+ callbacks.OH_AudioCapturer_OnStreamEvent = nullptr;
+ callbacks.OH_AudioCapturer_OnInterruptEvent = nullptr;
+ callbacks.OH_AudioCapturer_OnError = MyOnError;
+ // 设置音频输入流的回调
+ ret = OH_AudioStreamBuilder_SetCapturerCallback(m_builder, callbacks, this);
+ if(ret != AUDIOSTREAM_SUCCESS)
+ {
+ stopRecording();
+ qWarning() << "Set Capturer Callback failed!";
+ m_errorState = QAudio::FatalError;
+ return false;
+ }
+
+ ret = OH_AudioStreamBuilder_GenerateCapturer(m_builder, &m_capturer);
+ if(ret != AUDIOSTREAM_SUCCESS || !m_capturer)
+ {
+ stopRecording();
+ qWarning() << "Get Generate Capturer failed!";
+ m_errorState = QAudio::FatalError;
+ return false;
+ }
+
+ ret = OH_AudioCapturer_Start(m_capturer);//开始录制
+ if(ret != AUDIOSTREAM_SUCCESS)
+ {
+ stopRecording();
+ qWarning() << "Start Capturer Audio failed!";
+ m_errorState = QAudio::FatalError;
+ return false;
+ }
+
+ return true;
+}
+
+void QOpenharmonyAudioInput::stopRecording()
+{
+ if(m_builder)
+ {
+ OH_AudioStreamBuilder_Destroy(m_builder);
+ m_builder = nullptr;
+ }
+
+ if(m_capturer)
+ {
+ OH_AudioCapturer_Stop(m_capturer);
+ OH_AudioCapturer_Flush(m_capturer);//释放缓存数据
+ OH_AudioCapturer_Release(m_capturer);//释放录制实例
+ }
+
+ if (!m_pullMode && m_bufferIODevice) {
+ m_bufferIODevice->close();
+ delete m_bufferIODevice;
+ m_bufferIODevice = 0;
+ m_pushBuffer.clear();
+ }
+}
+
+OH_AudioStream_SampleFormat QOpenharmonyAudioInput::sampleSizeToFormatEnum(int sampleSize, QAudioFormat::SampleType sampleType)
+{
+#if OHOS_SDK_VERSION >= 17
+ if (QtOh::apiVersion() >= 17 && QAudioFormat::Float == sampleType)
+ return AUDIOSTREAM_SAMPLE_F32LE;
+#endif
+
+ switch (sampleSize)
+ {
+ case 8: return AUDIOSTREAM_SAMPLE_U8;
+ case 16: return AUDIOSTREAM_SAMPLE_S16LE;
+ case 24: return AUDIOSTREAM_SAMPLE_S24LE;
+ case 32: return AUDIOSTREAM_SAMPLE_S32LE;
+ default :break;
+ }
+
+ return AUDIOSTREAM_SAMPLE_S16LE;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,74 @@
+#ifndef QOPENHARMONYAUDIOINPUT_H
+#define QOPENHARMONYAUDIOINPUT_H
+
+#include <qaudiosystem.h>
+#include <QElapsedTimer>
+#include <ohaudio/native_audiocapturer.h>
+#include <ohaudio/native_audiostreambuilder.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenharmonyEngine;
+class QIODevice;
+class QBuffer;
+
+class QOpenharmonyAudioInput : public QAbstractAudioInput
+{
+ Q_OBJECT
+public:
+ QOpenharmonyAudioInput(const QByteArray &device);
+ ~QOpenharmonyAudioInput();
+
+ void start(QIODevice *device);
+ QIODevice *start();
+ void stop();
+ void reset();
+ void suspend();
+ void resume();
+ int bytesReady() const;
+ int periodSize() const;
+ void setBufferSize(int value);
+ int bufferSize() const;
+ void setNotifyInterval(int milliSeconds);
+ int notifyInterval() const;
+ qint64 processedUSecs() const;
+ qint64 elapsedUSecs() const;
+ QAudio::Error error() const;
+ QAudio::State state() const;
+ void setFormat(const QAudioFormat &format);
+ QAudioFormat format() const;
+
+ void setVolume(qreal volume);
+ qreal volume() const;
+
+public Q_SLOTS:
+ void processBuffer(const QByteArray& audioData);
+
+private:
+ bool startRecording();
+ void stopRecording();
+ void writeDataToDevice(const char *data, int size);
+ void flushBuffers();
+ OH_AudioStream_SampleFormat sampleSizeToFormatEnum(int sampleSize, QAudioFormat::SampleType sampleType);
+
+ QByteArray m_device;
+
+ bool m_pullMode;
+ qint64 m_processedBytes;
+ QIODevice *m_audioSource;
+ QBuffer *m_bufferIODevice;
+ QByteArray m_pushBuffer;
+ QAudio::Error m_errorState;
+ QAudio::State m_deviceState;
+ QElapsedTimer m_clockStamp;
+ qint64 m_lastNotifyTime;
+ qreal m_volume;
+ int m_intervalTime;
+ QAudioFormat m_format;
+
+ OH_AudioStreamBuilder *m_builder;
+ OH_AudioCapturer *m_capturer;
+};
+
+QT_END_NAMESPACE
+#endif // QOPENHARMONYAUDIOINPUT_H
new file mode 100644
@@ -0,0 +1,142 @@
+#include <QDebug>
+#include <QJsModule>
+
+#include "qopenharmonyaudiomanager.h"
+#include <private/qopenharmony_p.h>
+
+auto createRouting = []{
+ return QtOh::runOnJsUIThreadWithResult([]{
+ Napi::Object object = Napi::Object();
+ QJsModule module("@ohos.multimedia.audio");
+ Napi::Object obj = module.call("getAudioManager").As<Napi::Object>();
+ if (!obj.IsNull()) {
+ Napi::Value value = QJsObject(obj).call("getRoutingManager");
+ if (!value.IsNull()) {
+ object = value.As<Napi::Object>();
+ return object;
+ }
+ }
+
+ qWarning() << "get audio routing manager failed.";
+ return Napi::Object();
+ });
+};
+
+Q_GLOBAL_STATIC_WITH_ARGS(QJsObject, audioMgr, (createRouting()));
+
+
+enum class DeviceFlag {
+ ALL_DEVICES_FLAG = 3,
+ INPUT_DEVICES_FLAG = 2,
+ OUTPUT_DEVICES_FLAG = 1,
+};
+
+auto availableDevices = [](DeviceFlag flag)->Napi::Array {
+ return QtOh::runOnJsUIThreadWithResult([=]{
+ if (audioMgr->object().IsNull())
+ return Napi::Array();
+
+ Napi::Value devs;
+ devs = audioMgr->call("getDevicesSync", { Napi::Number::New(audioMgr->env(), int(flag)) });
+ return devs.As<Napi::Array>();
+ });
+};
+
+auto channelCounts(const QString &devName, DeviceFlag flag)->QList<int> {
+ return QtOh::runOnJsUIThreadWithResult([&](){
+ Napi::Array devices = availableDevices(flag);
+ QList<int> result;
+
+ for (uint32_t i = 0; i < devices.Length(); ++i){
+ Napi::Object obj = Napi::Value(devices[i]).ToObject();
+ if (obj.IsNull())
+ continue;
+
+ QJsObject dev(obj);
+ QString curName = QString::fromStdString(dev.get("displayName").ToString());
+ if(curName == devName){
+ Napi::Array channels = dev.get("channelCounts").As<Napi::Array>();
+ for (uint32_t j = 0; j < channels.Length(); ++j){
+ result.append(Napi::Value(channels[j]).ToNumber());
+ }
+ }
+ }
+ return result;
+ });
+}
+
+auto supportedSampleRates(const QString &devName, DeviceFlag flag)->QList<int> {
+ return QtOh::runOnJsUIThreadWithResult([&](){
+ Napi::Array devices = availableDevices(flag);
+ QList<int> result;
+ for (uint32_t i = 0; i < devices.Length(); ++i){
+ Napi::Object obj = Napi::Value(devices[i]).ToObject();
+ if (obj.IsNull())
+ continue;
+
+ QJsObject dev(obj);
+ QString curName = QString::fromStdString(dev.get("displayName").ToString());
+ if(curName == devName){
+ Napi::Array sampleRates = dev.get("sampleRates").As<Napi::Array>();
+ for (uint32_t j = 0; j < sampleRates.Length(); ++j){
+ result.append(Napi::Value(sampleRates[j]).ToNumber());
+ }
+ }
+ }
+ return result;
+ });
+};
+auto availableDeviceNames = [](DeviceFlag flag)->QList<QByteArray> {
+ return QtOh::runOnJsUIThreadWithResult([&](){
+ Napi::Array devices = availableDevices(flag);
+ QList<QByteArray> result;
+ for (uint32_t i = 0; i < devices.Length(); ++i) {
+ Napi::Object obj = Napi::Value(devices[i]).ToObject();
+ if (obj.IsNull())
+ continue;
+
+ QJsObject dev(obj);
+ int deviceType = dev.get("deviceType").ToNumber();
+ if(deviceType == 0){
+ continue;
+ }
+
+ QString devName = QString::fromStdString(dev.get("displayName").ToString());
+ if(!devName.isEmpty())
+ {
+ result.append(devName.toUtf8());
+ }
+ }
+ return result;
+ });
+};
+
+QList<QByteArray> QOpenharmonyAudioManager::availableInputDevices()
+{
+ return availableDeviceNames(DeviceFlag::INPUT_DEVICES_FLAG);
+}
+
+QList<QByteArray> QOpenharmonyAudioManager::availableOutputDevices()
+{
+ return availableDeviceNames(DeviceFlag::OUTPUT_DEVICES_FLAG);
+}
+
+QList<int> QOpenharmonyAudioManager::inputChannelCounts(const QString &devName)
+{
+ return channelCounts(devName, DeviceFlag::INPUT_DEVICES_FLAG);
+}
+
+QList<int> QOpenharmonyAudioManager::outputChannelCounts(const QString &devName)
+{
+ return channelCounts(devName, DeviceFlag::OUTPUT_DEVICES_FLAG);
+}
+
+QList<int> QOpenharmonyAudioManager::inputSupportedSampleRates(const QString &devName)
+{
+ return supportedSampleRates(devName, DeviceFlag::INPUT_DEVICES_FLAG);
+}
+
+QList<int> QOpenharmonyAudioManager::outputSupportedSampleRates(const QString &devName)
+{
+ return supportedSampleRates(devName, DeviceFlag::OUTPUT_DEVICES_FLAG);
+}
new file mode 100644
@@ -0,0 +1,18 @@
+#ifndef QOPENHARMONYAUDIOMANAGER_H
+#define QOPENHARMONYAUDIOMANAGER_H
+
+#include <QList>
+#include <QString>
+
+class QOpenharmonyAudioManager
+{
+public:
+ static QList<QByteArray> availableInputDevices();
+ static QList<QByteArray> availableOutputDevices();
+ static QList<int> inputChannelCounts(const QString &devName);
+ static QList<int> outputChannelCounts(const QString &devName);
+ static QList<int> inputSupportedSampleRates(const QString &devName);
+ static QList<int> outputSupportedSampleRates(const QString &devName);
+};
+
+#endif // QOPENHARMONYAUDIOMANAGER_H
new file mode 100644
@@ -0,0 +1,420 @@
+#include "qopenharmonyaudiooutput.h"
+#include "qopenharmonydeviceinfo.h"
+#include "qopenharmony.h"
+
+#include <QtEndian>
+#include <QtCore/QDataStream>
+#include <QtCore/qtimer.h>
+#include <private/qaudiohelpers_p.h>
+
+static int32_t AudioRendererOnWriteData(OH_AudioRenderer *renderer, void *userData, void *buffer, int32_t bufferLen)
+{
+ Q_UNUSED(renderer)
+
+ auto pSelf = reinterpret_cast<QOpenharmonyAudioOutput*>(userData);
+ if(pSelf)
+ {
+ pSelf->writeBuffer(buffer, bufferLen);
+ }
+
+ return 0;
+}
+
+// 自定义异常回调函数
+static int32_t AudioRendererOnError(OH_AudioRenderer *renderer, void* userData, OH_AudioStream_Result error)
+{
+ Q_UNUSED(renderer)
+ Q_UNUSED(userData)
+ qWarning() << "Audio output error code:" << error;
+ return 0;
+}
+
+QOpenharmonyAudioOutput::QOpenharmonyAudioOutput(const QByteArray &device)
+ : m_device(device)
+ , buffer_size(2500)
+ , period_size(0)
+ , totalBytes(0)
+ , pullMode(true)
+ , intervalTime(1000)
+ , lastNotifyTime(0)
+ , volumeCache(qreal(1.0))
+ , pRenderer(nullptr)
+ , pBuilder(nullptr)
+ , errorState(QAudio::NoError)
+ , audioSource(nullptr)
+ , deviceState(QAudio::StoppedState)
+{
+
+}
+
+QOpenharmonyAudioOutput::~QOpenharmonyAudioOutput()
+{
+ close();
+}
+
+void QOpenharmonyAudioOutput::writeBuffer(void *buffer, int length)
+{
+ period_size = length;
+ buffer_size = length;
+ int readLen = 0;
+ memset(buffer, 0, length);
+ if(pullMode)
+ {
+ readLen = audioSource->read((char *)buffer, length);
+ if(audioSource->atEnd() && (QAudio::ActiveState == deviceState))
+ {
+ deviceState = QAudio::IdleState;
+ emit stateChanged(deviceState);
+ }
+ }
+ else
+ {
+ mutex.lock();
+ if(length < selfBytes.size())
+ {
+ memcpy(buffer, selfBytes.data(), length);
+ readLen = length;
+ int len = selfBytes.size() - length;
+ selfBytes = selfBytes.right(len);
+ }
+ else
+ {
+ memcpy((char *)buffer, selfBytes.data(), selfBytes.size());
+ readLen = selfBytes.size();
+ selfBytes.clear();
+ }
+
+ if(0 == readLen && (QAudio::ActiveState == deviceState))
+ {
+ deviceState = QAudio::IdleState;
+ emit stateChanged(deviceState);
+ }
+
+ mutex.unlock();
+ }
+
+ totalBytes += readLen;
+
+ qint64 processedMsecs = processedUSecs() / 1000;
+ if (intervalTime && (processedMsecs - lastNotifyTime) >= intervalTime) {
+ emit notify();
+ lastNotifyTime = processedMsecs;
+ }
+}
+
+OH_AudioStream_SampleFormat QOpenharmonyAudioOutput::sampleSizeToFormatEnum(int sampleSize, QAudioFormat::SampleType sampleType)
+{
+#if OHOS_SDK_VERSION >= 17
+ if(QtOh::apiVersion() >= 17 && QAudioFormat::Float == sampleType)
+ return AUDIOSTREAM_SAMPLE_F32LE;
+#endif
+
+ switch(sampleSize)
+ {
+ case 8: return AUDIOSTREAM_SAMPLE_U8;
+ case 16: return AUDIOSTREAM_SAMPLE_S16LE;
+ case 24: return AUDIOSTREAM_SAMPLE_S24LE;
+ case 32: return AUDIOSTREAM_SAMPLE_S32LE;
+ default :break;
+ }
+
+ return AUDIOSTREAM_SAMPLE_S16LE;
+}
+
+
+bool QOpenharmonyAudioOutput::open()
+{
+ //创建
+ OH_AudioStream_Type type = AUDIOSTREAM_TYPE_RENDERER;
+ OH_AudioStream_Result result = OH_AudioStreamBuilder_Create(&pBuilder, AUDIOSTREAM_TYPE_RENDERER);
+ if(AUDIOSTREAM_SUCCESS != result)
+ {
+ qWarning() << "OH_Audio create builder error code : " << result;
+ close();
+ return false;
+ }
+
+ //设置参数
+ // 设置音频采样率
+ OH_AudioStreamBuilder_SetSamplingRate(pBuilder, settings.sampleRate());
+ // 设置音频声道
+ OH_AudioStreamBuilder_SetChannelCount(pBuilder, settings.channelCount());
+ // 设置音频采样格式
+ OH_AudioStreamBuilder_SetSampleFormat(pBuilder, sampleSizeToFormatEnum(settings.sampleSize(), settings.sampleType()));
+ //设置低延时
+ OH_AudioStreamBuilder_SetLatencyMode(pBuilder, AUDIOSTREAM_LATENCY_MODE_NORMAL);
+
+ OH_AudioStreamBuilder_SetFrameSizeInCallback(pBuilder, buffer_size);
+
+ //设置回调函数
+ OH_AudioRenderer_Callbacks rendererCallbacks;
+ memset(&rendererCallbacks, 0, sizeof(OH_AudioRenderer_Callbacks));
+ rendererCallbacks.OH_AudioRenderer_OnWriteData = AudioRendererOnWriteData;
+ rendererCallbacks.OH_AudioRenderer_OnStreamEvent = nullptr;
+ rendererCallbacks.OH_AudioRenderer_OnInterruptEvent = nullptr;
+ rendererCallbacks.OH_AudioRenderer_OnError = AudioRendererOnError;
+
+ result = OH_AudioStreamBuilder_SetRendererCallback(pBuilder, rendererCallbacks, this);
+ if(AUDIOSTREAM_SUCCESS != result)
+ {
+ qWarning() << "OH_Audio set callBack error code : " << result;
+ close();
+ return false;
+ }
+
+ // create OH_AudioRenderer
+ result = OH_AudioStreamBuilder_GenerateRenderer(pBuilder, &pRenderer);
+ if(AUDIOSTREAM_SUCCESS != result)
+ {
+ qWarning() << "OH_Audio Generate Renderer error code : " << result;
+ close();
+ return false;
+ }
+
+ // start
+ result = OH_AudioRenderer_Start(pRenderer);
+ if(AUDIOSTREAM_SUCCESS != result)
+ {
+ qWarning() << "OH_Audio Start error code : " << result;
+ close();
+ return false;
+ }
+
+ timeStampOpened.restart();
+ lastNotifyTime = 0;
+ totalBytes = 0;
+ return true;
+}
+
+void QOpenharmonyAudioOutput::close()
+{
+ if(!pullMode && audioSource)
+ {
+ delete audioSource;
+ audioSource = nullptr;
+ pullMode = true;
+ }
+
+ if(deviceState != QAudio::StoppedState)
+ {
+ OH_AudioRenderer_Stop(pRenderer);
+ }
+
+ if(pRenderer)
+ {
+ OH_AudioRenderer_Release(pRenderer);
+ pRenderer = nullptr;
+ }
+
+ if(pBuilder)
+ {
+ OH_AudioStreamBuilder_Destroy(pBuilder);
+ pBuilder = nullptr;
+ }
+}
+
+
+QIODevice* QOpenharmonyAudioOutput::start()
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = false;
+ selfBytes.clear();
+ audioSource = new OutputPrivate(this);
+ if(!audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered))
+ {
+ qWarning() << "open selfSource falid.";
+ errorState = QAudio::OpenError;
+ return nullptr;
+ }
+
+ if(!open())
+ {
+ errorState = QAudio::OpenError;
+ return nullptr;
+ }
+
+ deviceState = QAudio::IdleState;
+ emit stateChanged(deviceState);
+
+ return audioSource;
+}
+
+void QOpenharmonyAudioOutput::start(QIODevice* device)
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = true;
+ audioSource = device;
+
+ if(!open())
+ return;
+
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+}
+
+void QOpenharmonyAudioOutput::stop()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+
+ close();
+
+ deviceState = QAudio::StoppedState;
+
+ emit stateChanged(deviceState);
+}
+
+void QOpenharmonyAudioOutput::reset()
+{
+ if(deviceState == QAudio::ActiveState) {
+ OH_AudioRenderer_Flush(pRenderer);
+ }
+ //stop();
+}
+
+void QOpenharmonyAudioOutput::suspend()
+{
+ if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) {
+ OH_AudioRenderer_Pause(pRenderer);
+ deviceState = QAudio::SuspendedState;
+ errorState = QAudio::NoError;
+ emit stateChanged(deviceState);
+ }
+}
+
+void QOpenharmonyAudioOutput::resume()
+{
+ if (QAudio::SuspendedState == deviceState|| (QAudio::IdleState == deviceState)) {
+ OH_AudioRenderer_Start(pRenderer);//开始播放
+
+ deviceState = pullMode ? QAudio::ActiveState : QAudio::IdleState;
+ emit stateChanged(deviceState);
+ }
+}
+
+int QOpenharmonyAudioOutput::bytesFree() const
+{
+ return buffer_size;
+}
+
+int QOpenharmonyAudioOutput::periodSize() const
+{
+ return period_size;
+}
+
+void QOpenharmonyAudioOutput::setBufferSize(int value)
+{
+ if(deviceState == QAudio::StoppedState)
+ buffer_size = value;
+}
+
+int QOpenharmonyAudioOutput::bufferSize() const
+{
+ return buffer_size;
+}
+
+void QOpenharmonyAudioOutput::setNotifyInterval(int milliSeconds)
+{
+ intervalTime = qMax(0, milliSeconds);
+}
+
+int QOpenharmonyAudioOutput::notifyInterval() const
+{
+ return intervalTime;
+}
+
+qint64 QOpenharmonyAudioOutput::processedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+ return settings.durationForBytes(totalBytes);
+}
+
+qint64 QOpenharmonyAudioOutput::elapsedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+
+ return timeStampOpened.elapsed() * qint64(1000);
+}
+
+QAudio::Error QOpenharmonyAudioOutput::error() const
+{
+ return errorState;
+}
+
+QAudio::State QOpenharmonyAudioOutput::state() const
+{
+ return deviceState;
+}
+
+void QOpenharmonyAudioOutput::setVolume(qreal volume)
+{
+ if (qFuzzyCompare(volumeCache, volume))
+ return;
+
+ volumeCache = qBound(qreal(0), volume, qreal(1));
+}
+
+qreal QOpenharmonyAudioOutput::volume() const
+{
+ return volumeCache;
+}
+
+void QOpenharmonyAudioOutput::setFormat(const QAudioFormat& fmt)
+{
+ if (deviceState == QAudio::StoppedState)
+ settings = fmt;
+}
+
+QAudioFormat QOpenharmonyAudioOutput::format() const
+{
+ return settings;
+}
+
+qint64 QOpenharmonyAudioOutput::getAudioBuff(const char* data, qint64 len)
+{
+ mutex.lock();
+ selfBytes.append(data, len);
+ mutex.unlock();
+ return len;
+}
+
+
+OutputPrivate::OutputPrivate(QOpenharmonyAudioOutput* audio)
+{
+ audioDevice = qobject_cast<QOpenharmonyAudioOutput*>(audio);
+}
+
+OutputPrivate::~OutputPrivate() {}
+
+qint64 OutputPrivate::readData( char* data, qint64 len)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(len)
+
+ return 0;
+}
+
+qint64 OutputPrivate::writeData(const char* data, qint64 len)
+{
+ qint64 written = 0;
+
+ if((audioDevice->deviceState == QAudio::ActiveState)
+ ||(audioDevice->deviceState == QAudio::IdleState))
+ {
+ written = audioDevice->getAudioBuff(data, len);
+ audioDevice->deviceState = QAudio::ActiveState;
+ }
+ return written;
+}
new file mode 100644
@@ -0,0 +1,97 @@
+#ifndef QOPENHARMONYAUDIOOUTPUT_H
+#define QOPENHARMONYAUDIOOUTPUT_H
+
+#include <QAbstractAudioOutput>
+#include <QtCore/qdebug.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmutex.h>
+#include <QBuffer>
+#include <QByteArray>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qaudiosystem.h>
+
+#include <napi/native_api.h>
+#include <ohaudio/native_audiorenderer.h>
+#include <ohaudio/native_audiostreambuilder.h>
+
+class QOpenharmonyAudioOutput : public QAbstractAudioOutput
+{
+ Q_OBJECT
+public:
+ QOpenharmonyAudioOutput(const QByteArray &device);
+ ~QOpenharmonyAudioOutput();
+private:
+ OH_AudioStream_SampleFormat sampleSizeToFormatEnum(int sampleSize, QAudioFormat::SampleType sampleType);
+ //打开
+ bool open();
+ //关闭
+ void close();
+public:
+ QIODevice* start();
+ void start(QIODevice* device);
+ void stop();
+ void reset();
+ void suspend();
+ void resume();
+ int bytesFree() const;
+ int periodSize() const;
+ void setBufferSize(int value);
+ int bufferSize() const;
+ void setNotifyInterval(int milliSeconds);
+ int notifyInterval() const;
+ qint64 processedUSecs() const;
+ qint64 elapsedUSecs() const;
+ QAudio::Error error() const;
+ QAudio::State state() const;
+ void setVolume(qreal volume);
+ qreal volume() const;
+ void setFormat(const QAudioFormat& fmt);
+ QAudioFormat format() const;
+
+ qint64 getAudioBuff(const char* data, qint64 len);
+
+ void writeBuffer(void *buffer, int length);
+
+ QAudio::Error errorState;
+ QAudio::State deviceState;
+private:
+ QByteArray m_device;
+ QElapsedTimer timeStampOpened;
+ qint32 buffer_size;
+ qint32 period_size;
+ qint64 totalBytes;
+ bool pullMode;
+ qint64 intervalTime;
+ qint64 lastNotifyTime;
+ qreal volumeCache;
+
+ QMutex mutex;
+
+ OH_AudioRenderer * pRenderer;
+ OH_AudioStreamBuilder * pBuilder;
+
+ QByteArray selfBytes;
+ QIODevice* audioSource;
+ QAudioFormat settings;
+};
+
+class OutputPrivate : public QIODevice
+{
+ Q_OBJECT
+public:
+ OutputPrivate(QOpenharmonyAudioOutput* audio);
+ ~OutputPrivate();
+
+ qint64 readData( char* data, qint64 len);
+ qint64 writeData(const char* data, qint64 len);
+
+private:
+ QOpenharmonyAudioOutput *audioDevice;
+};
+
+#endif // QOPENHARMONYAUDIOOUTPUT_H
new file mode 100644
@@ -0,0 +1,82 @@
+#include "qopenharmonydeviceinfo.h"
+#include "qopenharmonyaudiomanager.h"
+
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+QOpenharmonyDeviceInfo::QOpenharmonyDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+ :m_device(device)
+ , m_mode(mode)
+{
+}
+
+QAudioFormat QOpenharmonyDeviceInfo::preferredFormat() const
+{
+ QAudioFormat format;
+ format.setCodec(QStringLiteral("audio/pcm"));
+ format.setSampleSize(16);
+ format.setSampleType(QAudioFormat::SignedInt);
+ format.setSampleRate(48000);
+ format.setChannelCount(2);
+
+ return format;
+}
+
+bool QOpenharmonyDeviceInfo::isFormatSupported(const QAudioFormat &format) const
+{
+ QOpenharmonyDeviceInfo *that = const_cast<QOpenharmonyDeviceInfo*>(this);
+ return that->supportedCodecs().contains(format.codec())
+ && that->supportedSampleRates().contains(format.sampleRate())
+ && that->supportedChannelCounts().contains(format.channelCount())
+ && that->supportedSampleSizes().contains(format.sampleSize())
+ && that->supportedByteOrders().contains(format.byteOrder())
+ && that->supportedSampleTypes().contains(format.sampleType());
+}
+
+QString QOpenharmonyDeviceInfo::deviceName() const
+{
+ return m_device;
+}
+
+QStringList QOpenharmonyDeviceInfo::supportedCodecs()
+{
+ return QStringList() << QStringLiteral("audio/pcm");
+}
+
+QList<int> QOpenharmonyDeviceInfo::supportedSampleRates()
+{
+ if (QAudio::AudioInput == m_mode) {
+ return QOpenharmonyAudioManager::inputSupportedSampleRates(QString::fromUtf8(m_device));
+ } else {
+ return QOpenharmonyAudioManager::outputSupportedSampleRates(QString::fromUtf8(m_device));
+ }
+}
+
+QList<int> QOpenharmonyDeviceInfo::supportedChannelCounts()
+{
+ if (QAudio::AudioInput == m_mode) {
+ return QOpenharmonyAudioManager::inputChannelCounts(QString::fromUtf8(m_device));
+ } else {
+ return QOpenharmonyAudioManager::outputChannelCounts(QString::fromUtf8(m_device));
+ }
+}
+
+QList<int> QOpenharmonyDeviceInfo::supportedSampleSizes()
+{
+ return QList<int>() << 8 << 16 << 24 << 32;
+}
+
+QList<QAudioFormat::Endian> QOpenharmonyDeviceInfo::supportedByteOrders()
+{
+ return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian;
+}
+
+QList<QAudioFormat::SampleType> QOpenharmonyDeviceInfo::supportedSampleTypes()
+{
+ return QList<QAudioFormat::SampleType>() << QAudioFormat::Float
+ << QAudioFormat::SignedInt
+ << QAudioFormat::UnSignedInt;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,35 @@
+#ifndef QOPENHARMONYDEVICEINFO_H
+#define QOPENHARMONYDEVICEINFO_H
+
+#include <qaudiosystem.h>
+#include <QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenHarmonyJsObject;
+
+class QOpenharmonyDeviceInfo : public QAbstractAudioDeviceInfo
+{
+ Q_OBJECT
+public:
+ QOpenharmonyDeviceInfo(const QByteArray &device, QAudio::Mode mode);
+ ~QOpenharmonyDeviceInfo() {};
+
+ QAudioFormat preferredFormat() const;
+ bool isFormatSupported(const QAudioFormat &format) const;
+ QString deviceName() const;
+ QStringList supportedCodecs();
+ QList<int> supportedSampleRates();
+ QList<int> supportedChannelCounts();
+ QList<int> supportedSampleSizes();
+ QList<QAudioFormat::Endian> supportedByteOrders();
+ QList<QAudioFormat::SampleType> supportedSampleTypes();
+
+private:
+ QByteArray m_device;
+ QAudio::Mode m_mode;
+ QSharedPointer<QOpenHarmonyJsObject> m_jsAudioMgr;
+};
+
+QT_END_NAMESPACE
+#endif // QOPENHARMONYDEVICEINFO_H
new file mode 100644
@@ -0,0 +1,42 @@
+#include "qopenharmonyengine.h"
+#include "qopenharmonyaudiomanager.h"
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QOpenharmonyEngine, openharmonyEngine);
+
+QOpenharmonyEngine::QOpenharmonyEngine()
+{
+}
+
+QOpenharmonyEngine::~QOpenharmonyEngine()
+{
+
+}
+
+QOpenharmonyEngine *QOpenharmonyEngine::instance()
+{
+ return openharmonyEngine();
+}
+
+QByteArray QOpenharmonyEngine::defaultDevice(QAudio::Mode mode) const
+{
+ const auto &devices = availableDevices(mode);
+ return !devices.isEmpty() ? devices.first() : QByteArray();
+}
+
+QList<QByteArray> QOpenharmonyEngine::availableDevices(QAudio::Mode mode) const
+{
+ QList<QByteArray> devices;
+ if (mode == QAudio::AudioInput) {
+ return QOpenharmonyAudioManager::availableInputDevices();
+ } else {
+ return QOpenharmonyAudioManager::availableOutputDevices();
+ }
+
+ return devices;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,26 @@
+#ifndef QOPENHARMONYENGINE_H
+#define QOPENHARMONYENGINE_H
+
+#include <qaudio.h>
+#include <qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenHarmonyJsObject;
+
+class QOpenharmonyEngine
+{
+public:
+ enum OutputValue { FramesPerBuffer, SampleRate };
+
+ QOpenharmonyEngine();
+ ~QOpenharmonyEngine();
+
+ static QOpenharmonyEngine *instance();
+
+ QByteArray defaultDevice(QAudio::Mode mode) const;
+ QList<QByteArray> availableDevices(QAudio::Mode mode) const;
+};
+
+QT_END_NAMESPACE
+#endif // QOPENHARMONYENGINE_H
new file mode 100644
@@ -0,0 +1,7 @@
+TEMPLATE = subdirs
+
+SUBDIRS += src
+
+qtHaveModule(quick) {
+ SUBDIRS += videonode
+}
new file mode 100644
@@ -0,0 +1,32 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qohaudioencodercontrol.h \
+ $$PWD/qohcameracontrol.h \
+ $$PWD/qohcameraimagecapturecontrol.h \
+ $$PWD/qohcamerainfocontrol.h \
+ $$PWD/qohcamerarendercontrol.h \
+ $$PWD/qohcamerasession.h \
+ $$PWD/qohcaptureservice.h \
+ $$PWD/qohcapturesession.h \
+ $$PWD/qohcontainercontrol.h \
+ $$PWD/qohdevicecontrol.h \
+ $$PWD/qohrecordercontrol.h \
+ $$PWD/qohvideoencodercontrol.h \
+ $$PWD/qohviewfindersettingscontrol2.h
+
+SOURCES += \
+ $$PWD/qohaudioencodercontrol.cpp \
+ $$PWD/qohcameracontrol.cpp \
+ $$PWD/qohcameraimagecapturecontrol.cpp \
+ $$PWD/qohcamerainfocontrol.cpp \
+ $$PWD/qohcamerarendercontrol.cpp \
+ $$PWD/qohcamerasession.cpp \
+ $$PWD/qohcaptureservice.cpp \
+ $$PWD/qohcapturesession.cpp \
+ $$PWD/qohcontainercontrol.cpp \
+ $$PWD/qohdevicecontrol.cpp \
+ $$PWD/qohrecordercontrol.cpp \
+ $$PWD/qohvideoencodercontrol.cpp \
+ $$PWD/qohviewfindersettingscontrol2.cpp
+
new file mode 100644
@@ -0,0 +1,119 @@
+#include "qohaudioencodercontrol.h"
+#include "qohcapturesession.h"
+#include <qaudiodeviceinfo.h>
+
+static int getnearsetValue(const QList<int> &supported, int value){
+ int offset = INT_MAX;
+ int ret;
+ for(int curValue : supported){
+ if(qAbs(value - curValue) <= offset)
+ {
+ offset = qAbs(value - curValue);
+ ret = value;
+ }
+ }
+
+ return ret;
+}
+
+QOhAudioEncoderControl::QOhAudioEncoderControl(QObject *parent)
+ :QAudioEncoderSettingsControl(parent)
+{
+ m_session = qobject_cast<QOhCaptureSession*>(parent);
+ update();
+}
+
+QStringList QOhAudioEncoderControl::supportedAudioCodecs() const
+{
+ return QStringList() << QLatin1String("audio/mp4a-latm") << QLatin1String("audio/g711mu");
+}
+
+QString QOhAudioEncoderControl::codecDescription(const QString &codecName) const
+{
+ if (QString::compare(codecName, QLatin1String("audio/mp4a-latm")) == 0)
+ return tr("AAC Low Complexity (AAC-LC) audio codec");
+
+ if (QString::compare(codecName, QLatin1String("audio/g711mu")) == 0)
+ return tr("G.711 μ-law audio codec");
+
+ return QString();
+}
+
+QList<int> QOhAudioEncoderControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
+{
+ Q_UNUSED(settings)
+ if (continuous)
+ *continuous = false;
+
+ return m_sampleRates;
+}
+
+QAudioEncoderSettings QOhAudioEncoderControl::audioSettings() const
+{
+ return m_session->audioSettings();
+}
+
+void QOhAudioEncoderControl::setAudioSettings(const QAudioEncoderSettings &settings)
+{
+ QAudioEncoderSettings tsettings(settings);
+ if(tsettings.channelCount() == 0)
+ tsettings.setChannelCount(2);
+
+ if (tsettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
+ tsettings.setCodec("audio/mp4a-latm");
+ switch (tsettings.quality()) {
+ case QMultimedia::VeryLowQuality:
+ tsettings.setSampleRate(8000);
+ break;
+ case QMultimedia::LowQuality:
+ tsettings.setSampleRate(22050);
+ break;
+ case QMultimedia::HighQuality:
+ tsettings.setSampleRate(48000);
+ break;
+ case QMultimedia::VeryHighQuality:
+ tsettings.setSampleRate(96000);
+ break;
+ case QMultimedia::NormalQuality:
+ default:
+ tsettings.setSampleRate(44100);
+ break;
+ }
+ }
+
+ if(!supportedAudioCodecs().contains(tsettings.codec()))
+ {
+ tsettings.setCodec("audio/mp4a-latm");
+ }
+
+ if (QString::compare(tsettings.codec(), QLatin1String("audio/mp4a-latm")) == 0){
+ tsettings.setBitRate( qBound(32000, settings.bitRate(), 500000) );
+ tsettings.setChannelCount( qBound(1, settings.channelCount(), 8) );
+ QList<int> supportSampleRate({8000, 11025, 12000, 16000, 22050, 24000, 32000,\
+ 44100, 48000, 64000, 88200, 96000});
+ tsettings.setSampleRate( getnearsetValue(supportSampleRate, tsettings.sampleRate()) );
+ }
+
+ if (QString::compare(tsettings.codec(), QLatin1String("audio/g711mu")) == 0){
+ tsettings.setBitRate( 64000 );
+ tsettings.setChannelCount( 1 );
+ tsettings.setSampleRate(8000);
+ }
+
+ m_session->setAudioSettings(tsettings);
+}
+
+void QOhAudioEncoderControl::update()
+{
+ m_sampleRates.clear();
+ QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
+ for (int i = 0; i < devices.size(); ++i) {
+ QList<int> rates = devices.at(i).supportedSampleRates();
+ for (int j = 0; j < rates.size(); ++j) {
+ int rate = rates.at(j);
+ if (!m_sampleRates.contains(rate))
+ m_sampleRates.append(rate);
+ }
+ }
+ std::sort(m_sampleRates.begin(), m_sampleRates.end());
+}
new file mode 100644
@@ -0,0 +1,34 @@
+#ifndef QOHAUDIOENCODERCONTROL_H
+#define QOHAUDIOENCODERCONTROL_H
+
+#include "qaudioencodersettingscontrol.h"
+#include <qaudioformat.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCaptureSession;
+
+class QOhAudioEncoderControl : public QAudioEncoderSettingsControl
+{
+ Q_OBJECT
+public:
+ QOhAudioEncoderControl(QObject *parent);
+ // virtual ~QOhAudioEncoderControl();
+
+ QStringList supportedAudioCodecs() const;
+ QString codecDescription(const QString &codecName) const;
+ QList<int> supportedSampleRates(const QAudioEncoderSettings &, bool *continuous = 0) const;
+
+ QAudioEncoderSettings audioSettings() const;
+ void setAudioSettings(const QAudioEncoderSettings&);
+
+private:
+ void update();
+
+ QOhCaptureSession* m_session;
+ QList<int> m_sampleRates;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHAUDIOENCODERCONTROL_H
new file mode 100644
@@ -0,0 +1,72 @@
+#include "qohcamerasession.h"
+#include "qohcameracontrol.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhCameraControl::QOhCameraControl(QOhCameraSession *session)
+ : QCameraControl(Q_NULLPTR)
+ , m_cameraSession(session)
+{
+ connect(m_cameraSession, SIGNAL(statusChanged(QCamera::Status)),
+ this, SIGNAL(statusChanged(QCamera::Status)));
+
+ connect(m_cameraSession, SIGNAL(stateChanged(QCamera::State)),
+ this, SIGNAL(stateChanged(QCamera::State)));
+
+ connect(m_cameraSession, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
+
+ connect(m_cameraSession, SIGNAL(captureModeChanged(QCamera::CaptureModes)),
+ this, SIGNAL(captureModeChanged(QCamera::CaptureModes)));
+}
+
+QOhCameraControl::~QOhCameraControl()
+{
+
+}
+
+QCamera::CaptureModes QOhCameraControl::captureMode() const
+{
+ return m_cameraSession->captureMode();
+}
+
+void QOhCameraControl::setCaptureMode(QCamera::CaptureModes mode)
+{
+ m_cameraSession->setCaptureMode(mode);
+}
+
+bool QOhCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
+{
+ return m_cameraSession->isCaptureModeSupported(mode);
+}
+
+void QOhCameraControl::setState(QCamera::State state)
+{
+ m_cameraSession->setState(state);
+}
+
+QCamera::State QOhCameraControl::state() const
+{
+ return m_cameraSession->state();
+}
+
+QCamera::Status QOhCameraControl::status() const
+{
+ return m_cameraSession->status();
+}
+
+bool QOhCameraControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const
+{
+ Q_UNUSED(status);
+
+ switch (changeType) {
+ case QCameraControl::CaptureMode:
+ case QCameraControl::ImageEncodingSettings:
+ case QCameraControl::VideoEncodingSettings:
+ case QCameraControl::Viewfinder:
+ return true;
+ default:
+ return false;
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,35 @@
+#ifndef QOHCAMERACONTROL_H
+#define QOHCAMERACONTROL_H
+
+#include <qcameracontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCameraSession;
+
+class QOhCameraControl : public QCameraControl
+{
+ Q_OBJECT
+
+public:
+ explicit QOhCameraControl(QOhCameraSession *session);
+ virtual ~QOhCameraControl();
+
+ QCamera::State state() const;
+ void setState(QCamera::State state);
+
+ QCamera::Status status() const;
+
+ QCamera::CaptureModes captureMode() const;
+ void setCaptureMode(QCamera::CaptureModes mode);
+ bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
+
+ bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const;
+
+private:
+ QOhCameraSession *m_cameraSession;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHCAMERACONTROL_H
new file mode 100644
@@ -0,0 +1,40 @@
+#include "qohcamerasession.h"
+#include "qohcameraimagecapturecontrol.h"
+
+QOhCameraImageCaptureControl::QOhCameraImageCaptureControl(QOhCameraSession *session)
+ :QCameraImageCaptureControl()
+ , m_session(session)
+{
+ connect(m_session, SIGNAL(readyForCaptureChanged(bool)), this, SIGNAL(readyForCaptureChanged(bool)));
+ connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
+ connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
+ connect(m_session, SIGNAL(imageMetadataAvailable(int,QString,QVariant)), this, SIGNAL(imageMetadataAvailable(int,QString,QVariant)));
+ connect(m_session, SIGNAL(imageAvailable(int,QVideoFrame)), this, SIGNAL(imageAvailable(int,QVideoFrame)));
+ connect(m_session, SIGNAL(imageSaved(int,QString)), this, SIGNAL(imageSaved(int,QString)));
+ connect(m_session, SIGNAL(imageCaptureError(int,int,QString)), this, SIGNAL(error(int,int,QString)));
+}
+
+bool QOhCameraImageCaptureControl::isReadyForCapture() const
+{
+ return m_session->isReadyForCapture();
+}
+
+QCameraImageCapture::DriveMode QOhCameraImageCaptureControl::driveMode() const
+{
+ return m_session->driveMode();
+}
+
+void QOhCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode)
+{
+ m_session->setDriveMode(mode);
+}
+
+int QOhCameraImageCaptureControl::capture(const QString &fileName)
+{
+ return m_session->capture(fileName);
+}
+
+void QOhCameraImageCaptureControl::cancelCapture()
+{
+ m_session->cancelCapture();
+}
new file mode 100644
@@ -0,0 +1,29 @@
+#ifndef QOHCAMERAIMAGECAPTURECONTROL_H
+#define QOHCAMERAIMAGECAPTURECONTROL_H
+
+#include <QCameraImageCaptureControl>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCameraSession;
+
+class QOhCameraImageCaptureControl : public QCameraImageCaptureControl
+{
+ Q_OBJECT
+public:
+ explicit QOhCameraImageCaptureControl(QOhCameraSession *session);
+
+ bool isReadyForCapture() const override;
+
+ QCameraImageCapture::DriveMode driveMode() const override;
+ void setDriveMode(QCameraImageCapture::DriveMode mode) override;
+
+ int capture(const QString &fileName) override;
+ void cancelCapture() override;
+
+private:
+ QOhCameraSession *m_session;
+};
+
+QT_END_NAMESPACE
+#endif // QOHCAMERAIMAGECAPTURECONTROL_H
new file mode 100644
@@ -0,0 +1,40 @@
+#include "qohcamerasession.h"
+#include "qohcamerainfocontrol.h"
+
+QT_BEGIN_NAMESPACE
+
+QCamera::Position QOhCameraInfoControl::cameraPosition(const QString &deviceName) const
+{
+ return position(deviceName);
+}
+
+int QOhCameraInfoControl::cameraOrientation(const QString &deviceName) const
+{
+ return orientation(deviceName);
+}
+
+QCamera::Position QOhCameraInfoControl::position(const QString &deviceName)
+{
+ const QList<OhCameraInfo> &cameras = QOhCameraSession::availableCameras();
+ for (int i = 0; i < cameras.count(); ++i) {
+ const OhCameraInfo &info = cameras.at(i);
+ if (QString::fromLatin1(info.name) == deviceName)
+ return info.position;
+ }
+
+ return QCamera::UnspecifiedPosition;
+}
+
+int QOhCameraInfoControl::orientation(const QString &deviceName)
+{
+ const QList<OhCameraInfo> &cameras = QOhCameraSession::availableCameras();
+ for (int i = 0; i < cameras.count(); ++i) {
+ const OhCameraInfo &info = cameras.at(i);
+ if (QString::fromLatin1(info.name) == deviceName)
+ return info.orientation;
+ }
+
+ return 0;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,22 @@
+#ifndef QOHCAMERAINFOCONTROL_H
+#define QOHCAMERAINFOCONTROL_H
+
+#include <qcamerainfocontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCameraInfoControl : public QCameraInfoControl
+{
+ Q_OBJECT
+
+public:
+ QCamera::Position cameraPosition(const QString &deviceName) const;
+ int cameraOrientation(const QString &deviceName) const;
+
+ static QCamera::Position position(const QString &deviceName);
+ static int orientation(const QString &deviceName);
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHCAMERAINFOCONTROL_H
new file mode 100644
@@ -0,0 +1,44 @@
+#include <arkui/native_node.h>
+#include <arkui/native_type.h>
+#include <arkui/native_interface.h>
+
+#include "qohcamerasession.h"
+#include "qohcamerarendercontrol.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhCameraRenderControl::QOhCameraRenderControl(QOhCameraSession* session, QObject *parent)
+ : QVideoRendererControl(parent)
+ , m_surface(nullptr)
+ , m_session(session)
+{
+}
+
+QOhCameraRenderControl::~QOhCameraRenderControl()
+{
+
+};
+
+QAbstractVideoSurface *QOhCameraRenderControl::surface() const
+{
+ return m_surface;
+}
+
+void QOhCameraRenderControl::setSurface(QAbstractVideoSurface *surface)
+{
+ m_surface = surface;
+ if(m_session)
+ {
+ m_session->setSurface(surface);
+ }
+}
+
+void QOhCameraRenderControl::setSession(QOhCameraSession *session)
+{
+ m_session = session;
+ if(session && m_surface)
+ {
+ m_session->setSurface(m_surface);
+ }
+}
+QT_BEGIN_NAMESPACE
new file mode 100644
@@ -0,0 +1,28 @@
+#ifndef QOHCAMERARENDERCONTROL_H
+#define QOHCAMERARENDERCONTROL_H
+
+#include <qvideorenderercontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCameraSession;
+
+class QOhCameraRenderControl : public QVideoRendererControl
+{
+ Q_OBJECT
+public:
+ QOhCameraRenderControl(QOhCameraSession* session, QObject *parent = nullptr);
+ ~QOhCameraRenderControl() override;
+
+ QAbstractVideoSurface *surface() const override;
+ void setSurface(QAbstractVideoSurface *surface) override;
+
+ void setSession(QOhCameraSession* session);
+
+private:
+ QAbstractVideoSurface *m_surface;
+ QOhCameraSession *m_session;
+};
+
+QT_END_NAMESPACE
+#endif // QOHCAMERARENDERCONTROL_H
new file mode 100644
@@ -0,0 +1,1112 @@
+#include "qohcamerasession.h"
+#include "ohmultimediautils.h"
+#include "../player/qohnativevideobuffer.h"
+#include <qelapsedtimer.h>
+#include <qfile.h>
+
+#include <QAbstractVideoSurface>
+#include <QtMultimedia/qvideosurfaceformat.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+
+#include "ohcamera/camera.h"
+#include "ohcamera/camera_input.h"
+#include "ohcamera/capture_session.h"
+#include "ohcamera/photo_output.h"
+#include "ohcamera/preview_output.h"
+#include "ohcamera/video_output.h"
+#include "ohcamera/camera_manager.h"
+#include <qguiapplication.h>
+
+#include <multimedia/image_framework/image/image_native.h>
+#include <multimedia/image_framework/image/image_receiver_native.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QList<OhCameraInfo>, deviceList)
+
+Camera_Manager *QOhCameraSession::g_cameraManager = nullptr;
+Camera_Device *QOhCameraSession::g_cameras = nullptr;
+uint32_t QOhCameraSession::g_camerasCount = 0;
+
+QOhCameraSession *p_cameraSession = nullptr;
+void OnCallback(OH_ImageReceiverNative *receiver){
+ if(p_cameraSession)
+ p_cameraSession->onReceiverCallback(receiver);
+}
+
+QVideoFrame imageNativeToVideoFrame(OH_ImageNative *image, Camera_Format fromat) {
+ Image_Size imgSize;
+ Image_ErrorCode errCode = OH_ImageNative_GetImageSize(image, &imgSize);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "Failed to get image size, error code:" << errCode;
+ return QVideoFrame();
+ }
+
+ size_t componentTypeSize = 0;
+ OH_ImageNative_GetComponentTypes(image, nullptr, &componentTypeSize);
+ if (componentTypeSize == 0) {
+ qWarning() << "Failed to get image component type size";
+ return QVideoFrame();
+ }
+ uint32_t* components = new uint32_t[componentTypeSize];
+ errCode = OH_ImageNative_GetComponentTypes(image, &components, &componentTypeSize);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "Failed to get image component types, error code:" << errCode;
+ delete[] components;
+ return QVideoFrame();
+ }
+
+ OH_NativeBuffer* nativeBuffer = nullptr;
+ errCode = OH_ImageNative_GetByteBuffer(image, components[0], &nativeBuffer);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "Failed to get image byte buffer, error code:" << errCode;
+ delete[] components;
+ return QVideoFrame();
+ }
+
+ size_t nativeBufferSize = 0;
+ errCode = OH_ImageNative_GetBufferSize(image, components[0], &nativeBufferSize);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "Failed to get buffer size, error code:" << errCode;
+ delete[] components;
+ return QVideoFrame();
+ }
+
+ int32_t rowStride = 0;
+ errCode = OH_ImageNative_GetRowStride(image, components[0], &rowStride);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "Failed to get row stride, error code:" << errCode;
+ delete[] components;
+ return QVideoFrame();
+ }
+ delete[] components;
+
+ void* srcVir = nullptr;
+ OH_NativeBuffer_Map(nativeBuffer, &srcVir);
+ uint8_t* srcBuffer = static_cast<uint8_t*>(srcVir);
+
+ QVideoFrame result;
+ switch (fromat) {
+ case CAMERA_FORMAT_YUV_420_SP: {
+ OH_NativeBuffer_Config config{};
+ OH_NativeBuffer_GetConfig(nativeBuffer, &config);
+ const int dataSize = config.stride * config.height * 3 / 2;
+ uchar *dataCopy = new uchar[dataSize];
+ memcpy(dataCopy, srcBuffer, dataSize);
+ OH_NativeBuffer_Unmap(nativeBuffer);
+ auto *videoBuffer = new QOhNativeVideoBuffer(dataCopy, dataSize, config, config.stride);
+ QVideoFrame nv21Frame(videoBuffer, QSize(imgSize.width, imgSize.height), QVideoFrame::Format_NV21);
+ QImage argb = nv21Frame.image();
+ if (!argb.isNull())
+ result = QVideoFrame(argb);
+ return result;
+ }
+ case CAMERA_FORMAT_JPEG: {
+ uchar *dataCopy = new uchar[nativeBufferSize];
+ memcpy(dataCopy, srcBuffer, nativeBufferSize);
+ OH_NativeBuffer_Unmap(nativeBuffer);
+ QImage img;
+ img.loadFromData(dataCopy, static_cast<int>(nativeBufferSize), "JPEG");
+ delete[] dataCopy;
+ if (!img.isNull())
+ result = QVideoFrame(img);
+ return result;
+ }
+ case CAMERA_FORMAT_RGBA_8888:
+ result = QVideoFrame(nativeBufferSize, QSize(imgSize.width, imgSize.height),
+ rowStride, QVideoFrame::Format_RGB32);
+ if (result.map(QAbstractVideoBuffer::WriteOnly)) {
+ memcpy(result.bits(), srcBuffer, result.mappedBytes());
+ result.unmap();
+ }
+ break;
+ default:
+ break;
+ }
+ OH_NativeBuffer_Unmap(nativeBuffer);
+ return result;
+}
+
+QOhCameraSession::QOhCameraSession(QObject *parent) : QObject{ parent }
+ , m_state(QCamera::UnloadedState)
+ , m_savedState(-1)
+ , m_status(QCamera::UnloadedStatus)
+ , m_captureMode(QCamera::CaptureStillImage)
+ , m_captureCanceled(false)
+ , m_lastImageCaptureId(0)
+ , m_captureDestination(QCameraImageCapture::CaptureToFile)
+ , m_captureImageDriveMode(QCameraImageCapture::SingleImageCapture)
+ , m_readyForCapture(false)
+ , m_currentImageCaptureId(-1)
+{
+ p_cameraSession = this;
+
+ m_mediaStorageLocation.addStorageLocation(
+ QMediaStorageLocation::Pictures,
+ OhMultimediaUtils::getDefaultMediaDirectory(OhMultimediaUtils::Image));
+
+ if (qApp) {
+ connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
+ this, SLOT(onApplicationStateChanged(Qt::ApplicationState)));
+ }
+ initCameraSession();
+ m_surfaceID[0] = '\0';
+}
+
+QOhCameraSession::~QOhCameraSession()
+{
+ if(m_state != QCamera::UnloadedState)
+ {
+ unload();
+ }
+
+ if(g_cameraManager && cameraOutputCapability_)
+ {
+ OH_CameraManager_DeleteSupportedCameraOutputCapability(g_cameraManager, cameraOutputCapability_);
+ cameraOutputCapability_ = nullptr;
+ }
+
+ if(g_cameras)
+ {
+ OH_CameraManager_DeleteSupportedCameras(g_cameraManager, g_cameras, g_camerasCount);
+ g_cameras = nullptr;
+ g_camerasCount = 0;
+ deviceList->clear();
+ }
+
+ if(captureSession_)
+ {
+ OH_CaptureSession_Release(captureSession_);
+ captureSession_ = nullptr;
+ }
+
+ if(g_cameraManager)
+ {
+ OH_Camera_DeleteCameraManager(g_cameraManager);
+ g_cameraManager = nullptr;
+ }
+
+ p_cameraSession = nullptr;
+}
+
+const QList<OhCameraInfo> &QOhCameraSession::availableCameras()
+{
+ if (deviceList->isEmpty())
+ updateAvailableCameras();
+
+ return *deviceList;
+}
+
+void QOhCameraSession::setState(QCamera::State state)
+{
+ if (m_state == state)
+ return;
+
+ // If the application is inactive, the camera shouldn't be started. Save the desired state
+ // instead and it will be set when the application becomes active.
+ ////TODO applicationStateChanged信号无法触发,暂时不做判断
+ // if (qApp->applicationState() == Qt::ApplicationActive)
+ // setStateHelper(state);
+ // else
+ // m_savedState = state;
+
+ setStateHelper(state);
+}
+
+void QOhCameraSession::setDevice(const QString &device)
+{
+ m_sourceDeviceName = device;
+ if(!g_cameraManager) return;
+
+ if(cameraOutputCapability_)
+ {
+ OH_CameraManager_DeleteSupportedCameraOutputCapability(g_cameraManager, cameraOutputCapability_);
+ cameraOutputCapability_ = nullptr;
+ unload();
+ }
+
+ if(!g_cameras)
+ {
+ updateAvailableCameras();
+ }
+
+ m_deviceIndex = (m_sourceDeviceName == QLatin1String("default") ? 0 : -1);
+ for(uint32_t i = 0; i < g_camerasCount; ++i)
+ {
+ if(QString(g_cameras[i].cameraId) == m_sourceDeviceName)
+ {
+ m_deviceIndex = i;
+ }
+ }
+
+ if(m_deviceIndex < 0)
+ {
+ qWarning() << QString("camera %1 not exsists!").arg(m_sourceDeviceName);
+ return;
+ }
+
+ Camera_ErrorCode ret;
+ ret = OH_CameraManager_GetSupportedCameraOutputCapability(g_cameraManager, &g_cameras[m_deviceIndex],
+ &cameraOutputCapability_);
+
+ if (!cameraOutputCapability_ || ret != CAMERA_OK \
+ || cameraOutputCapability_->previewProfilesSize == 0) {
+ qWarning() << "OH_CameraManager_GetSupportedCameraOutputCapability falied!";
+ return;
+ }
+
+ Q_EMIT loaded();
+}
+
+void QOhCameraSession::setStateHelper(QCamera::State state)
+{
+ bool succeeded = false;
+ switch (state) {
+ case QCamera::UnloadedState:
+ succeeded = unload();
+ break;
+ case QCamera::LoadedState:
+ case QCamera::ActiveState:
+ if (m_state == QCamera::UnloadedState && !load())
+ return;
+
+ if (state == QCamera::ActiveState)
+ succeeded = startPreview();
+ else if (state == QCamera::LoadedState)
+ succeeded = stopPreview();
+ break;
+ }
+
+ if (succeeded) {
+ m_state = state;
+ emit stateChanged(m_state);
+
+ QCamera::Status newStatus;
+ switch (state) {
+ case QCamera::ActiveState:
+ newStatus = QCamera::ActiveStatus;
+ break;
+ case QCamera::LoadedState:
+ newStatus = QCamera::LoadedStatus;
+ break;
+ case QCamera::UnloadedState:
+ default:
+ newStatus = QCamera::UnloadedStatus;
+ break;
+ }
+ if (m_status != newStatus) {
+ m_status = newStatus;
+ emit statusChanged(m_status);
+ }
+ }
+}
+
+void QOhCameraSession::initCameraSession()
+{
+ Camera_ErrorCode ret;
+
+ if(!g_cameraManager){
+ ret = OH_Camera_GetCameraManager(&g_cameraManager);
+ if (!g_cameraManager || ret != CAMERA_OK)
+ {
+ qWarning() << "OH_Camera_GetCameraManager failed!";
+ return;
+ }
+ }
+
+ ret = OH_CameraManager_CreateCaptureSession(g_cameraManager, &captureSession_);
+ if (captureSession_ == nullptr || ret != CAMERA_OK)
+ {
+ qWarning() << "OH_CameraManager_CreateCaptureSession failed!";
+ return;
+ }
+}
+
+bool QOhCameraSession::initReceiver(uint32_t width, uint32_t height)
+{
+ // 创建 OH_ImageReceiverOptions 实例
+ m_recOptions = nullptr;
+ Image_ErrorCode errCode = OH_ImageReceiverOptions_Create(&m_recOptions);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "ImageReceiverNativeCTest create image receiver options failed, errCode: "<<errCode;
+ return false;
+ }
+
+ Image_Size imgSize;
+ imgSize.width = width;
+ imgSize.height = height;
+
+ // 设置 OH_ImageReceiverOptions 的 size 属性
+ errCode = OH_ImageReceiverOptions_SetSize(m_recOptions, imgSize);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "ImageReceiverNativeCTest set image receiver options size failed, errCode: "<< errCode;
+ stopReceiver();
+ return false;
+ }
+
+ // 设置 OH_ImageReceiverOptions 的 capacity 属性
+ int32_t image_capcity = 8;
+ errCode = OH_ImageReceiverOptions_SetCapacity(m_recOptions, image_capcity);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "ImageReceiverNativeCTest set image receiver options capacity failed, errCode: "<< errCode;
+ stopReceiver();
+ return false;
+ }
+
+ // 创建 OH_ImageReceiverNative 实例
+ m_receiver = nullptr;
+ errCode = OH_ImageReceiverNative_Create(m_recOptions, &m_receiver);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "ImageReceiverNativeCTest create image receiver failed, errCode: "<< errCode;
+ stopReceiver();
+ return false;
+ }
+
+ // 注册一个回调事件,每当接收到新的图片,该回调事件就会响应。
+ errCode = OH_ImageReceiverNative_On(m_receiver, OnCallback);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "ImageReceiverNativeCTest image receiver on failed, errCode: "<< errCode;
+ stopReceiver();
+ return false;
+ }
+
+ // 读取 OH_ImageReceiverNative 的 surfaceID 属性
+ uint64_t surfaceID = 0;
+ errCode = OH_ImageReceiverNative_GetReceivingSurfaceId(m_receiver, &surfaceID);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "ImageReceiverNativeCTest get image receiver surfaceID failed, errCode: "<< errCode;
+ stopReceiver();
+ return false;
+ }
+
+ sprintf(m_surfaceID, "%ld", surfaceID);
+ return true;
+}
+
+void QOhCameraSession::stopReceiver()
+{
+ if(m_recOptions)
+ {
+ OH_ImageReceiverOptions_Release(m_recOptions);
+ m_recOptions = nullptr;
+ }
+ if(m_receiver)
+ {
+ OH_ImageReceiverNative_Release(m_receiver);
+ m_receiver = nullptr;
+ }
+
+ m_surfaceID[0] = '\0';
+}
+
+void QOhCameraSession::processCapturedImage(int id, const QVideoFrame &data,\
+ QCameraImageCapture::CaptureDestinations dest, const QString &fileName)
+{
+ if (dest & QCameraImageCapture::CaptureToFile) {
+
+
+ const QString actualFileName = m_mediaStorageLocation.generateFileName(fileName,
+ QMediaStorageLocation::Pictures,
+ QLatin1String("IMG_"),
+ QLatin1String("jpg"));
+ QImage image = data.image();
+ QFile file(actualFileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ const QString errorMessage = tr("Could not open destination file: %1").arg(actualFileName);
+ emit imageCaptureError(id, QCameraImageCapture::ResourceError, errorMessage);
+ return;
+ }
+
+ if (!image.save(&file, "JPG")) {
+ emit imageCaptureError(id, QCameraImageCapture::OutOfSpaceError, file.errorString());
+ file.close();
+ return;
+ }
+
+ file.close();
+ emit imageSaved(id, actualFileName);
+ }
+
+ if (dest & QCameraImageCapture::CaptureToBuffer) {
+ emit imageAvailable(id, data);
+ }
+}
+
+QCameraViewfinderSettings QOhCameraSession::profileToSetting(Camera_Profile *profile)
+{
+ QCameraViewfinderSettings s(m_viewfinderSettings);
+
+ s.setResolution(QSize(profile->size.width, profile->size.height));
+ s.setPixelAspectRatio(QSize(1, 1));
+ QVideoFrame::PixelFormat sformat;
+
+ switch(profile->format){
+ case CAMERA_FORMAT_RGBA_8888: sformat = QVideoFrame::Format_RGB32;break;
+ case CAMERA_FORMAT_YUV_420_SP: sformat = QVideoFrame::Format_NV21;break;
+ case CAMERA_FORMAT_JPEG: sformat = QVideoFrame::Format_Jpeg;break;
+ case CAMERA_FORMAT_YCBCR_P010:
+ case CAMERA_FORMAT_YCRCB_P010:
+ sformat = QVideoFrame::Format_User;
+ break;
+ default:
+ sformat = QVideoFrame::Format_Invalid;
+ }
+ s.setPixelFormat(sformat);
+ s.setMinimumFrameRate(30);
+ s.setMaximumFrameRate(30);
+
+ return s;
+}
+
+
+void QOhCameraSession::setCaptureMode(QCamera::CaptureModes mode)
+{
+ m_captureMode = mode;
+}
+
+bool QOhCameraSession::isCaptureModeSupported(QCamera::CaptureModes mode) const
+{
+ return true;
+}
+
+bool QOhCameraSession::isReadyForCapture()
+{
+ return m_status == QCamera::ActiveStatus;
+}
+
+int QOhCameraSession::capture(const QString &fileName)
+{
+ ++m_lastImageCaptureId;
+
+ if (m_captureImageDriveMode == QCameraImageCapture::SingleImageCapture) {
+
+ if (!photoOutput_) {
+ emit imageCaptureError(m_lastImageCaptureId, QCameraImageCapture::NotReadyError,
+ tr("Photo output not ready"));
+ return m_lastImageCaptureId;
+ }
+
+ m_currentImageCaptureId = m_lastImageCaptureId;
+ m_currentImageCaptureFileName = fileName;
+
+ emit imageExposed(m_currentImageCaptureId);
+
+ Camera_ErrorCode ret = OH_PhotoOutput_Capture(photoOutput_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_PhotoOutput_Capture failed." << ret;
+ emit imageCaptureError(m_currentImageCaptureId, QCameraImageCapture::ResourceError,
+ tr("Capture request failed"));
+ }
+ } else {
+ //: Drive mode is the camera's shutter mode, for example single shot, continuos exposure, etc.
+ emit imageCaptureError(m_lastImageCaptureId, QCameraImageCapture::NotSupportedFeatureError,
+ tr("Drive mode not supported"));
+ }
+
+ return m_lastImageCaptureId;
+}
+
+void QOhCameraSession::cancelCapture()
+{
+ m_captureCanceled = true;
+}
+
+QCameraImageCapture::DriveMode QOhCameraSession::driveMode() const
+{
+ return m_captureImageDriveMode;
+}
+
+void QOhCameraSession::setDriveMode(QCameraImageCapture::DriveMode mode)
+{
+ m_captureImageDriveMode = mode;
+}
+
+void QOhCameraSession::setReadyForCapture(bool ready)
+{
+ if (m_readyForCapture == ready)
+ return;
+
+ m_readyForCapture = ready;
+ emit readyForCaptureChanged(ready);
+}
+
+QCameraImageCapture::CaptureDestinations QOhCameraSession::captureDestination() const
+{
+ return m_captureDestination;
+}
+
+void QOhCameraSession::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
+{
+ if (m_captureDestination != destination) {
+ m_captureDestination = destination;
+ emit captureDestinationChanged(m_captureDestination);
+ }
+}
+
+QCameraViewfinderSettings QOhCameraSession::viewfinderSettings() const
+{
+ return m_status == QCamera::ActiveStatus ? m_actualViewfinderSettings : m_viewfinderSettings;
+}
+
+void QOhCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings)
+{
+ if (m_viewfinderSettings == settings)
+ return;
+
+ m_viewfinderSettings = m_actualViewfinderSettings = settings;
+}
+
+void QOhCameraSession::setSurface(QAbstractVideoSurface *surface)
+{
+ if(m_surface == surface)
+ return;
+
+ m_surface = surface;
+}
+
+void QOhCameraSession::onReceiverCallback(OH_ImageReceiverNative *receiver)
+{
+ if(!m_surface) return;
+
+ // 读取 OH_ImageReceiverNative 的下一个图片对象
+ OH_ImageNative* image = nullptr;
+ Image_ErrorCode errCode = OH_ImageReceiverNative_ReadNextImage(receiver, &image);
+ if (errCode != IMAGE_SUCCESS) {
+ qWarning() << "OnCallback ImageReceiverNativeCTest get image receiver next image failed, errCode: "<< errCode;
+ return;
+ }
+
+ if (profile_) {
+ QVideoFrame frame = imageNativeToVideoFrame(image, profile_->format);
+
+ m_presentMutex.lock();
+ m_currentFrame = frame;
+ m_presentMutex.unlock();
+
+ QMetaObject::invokeMethod(this, "presentFrame", Qt::QueuedConnection);
+ }
+
+ // 释放 OH_ImageNative 实例
+ errCode = OH_ImageNative_Release(image);
+}
+
+void QOhCameraSession::onPhotoCallback(Camera_PhotoOutput *photoOutput, OH_PhotoNative *photo)
+{
+ Q_UNUSED(photoOutput)
+ if (!m_captureCanceled) {
+ if (!photoProfile_) {
+ qWarning() << "onPhotoCallback: photoProfile_ is null";
+ return;
+ }
+
+ OH_ImageNative* imageNative;
+ Camera_ErrorCode errCode = OH_PhotoNative_GetMainImage(photo, &imageNative);
+ if (errCode != CAMERA_OK) {
+ qWarning() << "OH_PhotoNative_GetMainImage get image failed, errCode: "<< errCode;
+ return;
+ }
+ QVideoFrame frame = imageNativeToVideoFrame(imageNative, photoProfile_->format);
+
+ if (frame.isValid()) {
+ emit imageCaptured(m_currentImageCaptureId, frame.image());
+ }
+
+ QtConcurrent::run(this, &QOhCameraSession::processCapturedImage,
+ m_currentImageCaptureId,
+ frame,
+ m_captureDestination,
+ m_currentImageCaptureFileName);
+ }
+
+ m_captureCanceled = false;
+}
+
+bool QOhCameraSession::addVideoOutput(const QString &surfaceID, const QSize &reslotion)
+{
+ Camera_VideoOutput *videoOutput = nullptr;
+ if(!g_cameraManager) return false;
+
+ qDebug() << "record video surfaceID:" << surfaceID;
+ if(m_sessionStarted)
+ {
+ OH_CaptureSession_Stop(captureSession_);
+ }
+
+ Camera_VideoProfile *videoProfile = nullptr;
+ int offset = INT_MAX;
+ int usrPix = reslotion.width() * reslotion.height();
+ for(int i = 0; i < cameraOutputCapability_->videoProfilesSize; ++i)
+ {
+ Camera_VideoProfile *curPro = cameraOutputCapability_->videoProfiles[i];
+ if(curPro->size.width > 4096 || curPro->size.height > 4096) continue;
+
+ int curPix = curPro->size.width * curPro->size.height;
+ if(qAbs(curPix - usrPix) < offset)
+ {
+ offset = qAbs(curPix - usrPix);
+ videoProfile = curPro;
+ }
+ }
+ if (!videoProfile) {
+ qWarning() << "Get previewProfiles failed.";
+ return false;
+ }
+ //videoProfile = cameraOutputCapability->videoProfiles[0];
+
+ Camera_ErrorCode ret;
+ ret = OH_CaptureSession_BeginConfig(captureSession_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_BeginConfig failed.";
+ return false;
+ }
+
+ if(!cameraInput_){
+ ret = OH_CameraManager_CreateCameraInput(g_cameraManager, &g_cameras[m_deviceIndex], &cameraInput_);
+ if (!cameraInput_ || ret != CAMERA_OK) {
+ qWarning() << "CreateCameraInput failed.";
+ return false;
+ }
+
+ ret = OH_CameraInput_Open(cameraInput_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "CameraInput_Open failed.";
+ return false;
+ }
+
+ ret = OH_CaptureSession_AddInput(captureSession_, cameraInput_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_AddInput failed.";
+ return false;
+ }
+ }
+
+ ret = OH_CameraManager_CreateVideoOutput(g_cameraManager, videoProfile,\
+ surfaceID.toLatin1().data(), &videoOutput);
+ if (videoProfile == nullptr || videoOutput == nullptr || ret != CAMERA_OK) {
+ qWarning() << "OH_CameraManager_CreateVideoOutput failed.";
+ }
+
+ ret = OH_CaptureSession_AddVideoOutput(captureSession_, videoOutput);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_AddVideoOutput failed.";
+ return false;
+ }
+
+ ret = OH_CaptureSession_CommitConfig(captureSession_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_CommitConfig failed.";
+ return false;
+ }
+
+ ret = OH_CaptureSession_Start(captureSession_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_VideoOutput_Start failed.";
+ return false;
+ }
+
+ ret = OH_VideoOutput_Start(videoOutput);
+ m_sessionStarted = true;
+
+ videoOutput_ = videoOutput;
+ return true;
+}
+
+bool QOhCameraSession::removeVideoOutput()
+{
+ OH_CaptureSession_Stop(captureSession_);
+
+ if(captureSession_ && videoOutput_){
+ OH_CaptureSession_BeginConfig(captureSession_);
+ OH_CaptureSession_RemoveVideoOutput(captureSession_, videoOutput_);
+ OH_CaptureSession_CommitConfig(captureSession_);
+ }
+
+ if (videoOutput_) {
+ OH_VideoOutput_Stop(videoOutput_);
+ OH_VideoOutput_Release(videoOutput_);
+ videoOutput_ = nullptr;
+ }
+
+ if(m_state == QCamera::ActiveState)
+ {
+ OH_CaptureSession_Start(captureSession_);
+ m_sessionStarted = true;
+ }
+
+ return true;
+}
+
+bool QOhCameraSession::createPreviewOutput()
+{
+ int offset = INT_MAX;
+ int usrPix = m_viewfinderSettings.resolution().width() * m_viewfinderSettings.resolution().height();
+
+ Camera_Format defaultFormat;
+ switch(m_viewfinderSettings.pixelFormat()){
+ case QVideoFrame::Format_RGB32: defaultFormat = CAMERA_FORMAT_RGBA_8888;break;
+ case QVideoFrame::Format_NV21: defaultFormat = CAMERA_FORMAT_YUV_420_SP;break;
+ case QVideoFrame::Format_Jpeg: defaultFormat = CAMERA_FORMAT_JPEG;break;
+ default:
+ defaultFormat = CAMERA_FORMAT_YUV_420_SP;
+ }
+
+ for(int i = 0; i < cameraOutputCapability_->previewProfilesSize; ++i)
+ {
+ Camera_Profile *curPro = cameraOutputCapability_->previewProfiles[i];
+ int curPix = curPro->size.width * curPro->size.height;
+
+ if (!profile_) {
+ offset = qAbs(curPix - usrPix);
+ profile_ = curPro;
+ continue;
+ }
+
+ if (curPro->format == defaultFormat) {
+ if(profile_->format != defaultFormat){
+ offset = qAbs(curPix - usrPix);
+ profile_ = curPro;
+ continue;
+ }
+ } else if (profile_->format == defaultFormat) {
+ continue;
+ }
+
+ if (qAbs(curPix - usrPix) < offset) {
+ offset = qAbs(curPix - usrPix);
+ profile_ = curPro;
+ }
+ }
+ if (!profile_) {
+ qWarning() << "Get previewProfiles failed.";
+ return false;
+ }
+
+ m_actualViewfinderSettings = profileToSetting(profile_);
+
+ initReceiver(profile_->size.width, profile_->size.height);
+ if (m_surface) {
+ if (m_surfaceID[0] != '\0') {
+ Camera_ErrorCode ret;
+ ret = OH_CameraManager_CreatePreviewOutput(g_cameraManager, profile_, m_surfaceID, &previewOutput_);
+ if (m_surfaceID[0] == '\0' || !previewOutput_ || ret != CAMERA_OK) {
+ qWarning() << "CreatePreviewOutput failed or surfaceID not valid.";
+ return false;
+ }
+ }
+ else {
+ qWarning() << "surface not ready.";
+ return false;
+ }
+ } else {
+ qWarning() << "surface not set.";
+ return false;
+ }
+
+ return true;
+}
+
+bool QOhCameraSession::createPhotoOutput()
+{
+ int maxPix = 0;
+
+ for (int i = 0; i < cameraOutputCapability_->photoProfilesSize; ++i)
+ {
+ Camera_Profile *curPro = cameraOutputCapability_->photoProfiles[i];
+ int curPix = curPro->size.width * curPro->size.height;
+ if (curPix > maxPix) {
+ maxPix = curPix;
+ photoProfile_ = curPro;
+ }
+ }
+
+ if (!photoProfile_) {
+ qWarning() << "get photoProfile failed.";
+ return false;
+ }
+
+ Camera_ErrorCode ret;
+ ret = OH_CameraManager_CreatePhotoOutputWithoutSurface(g_cameraManager, photoProfile_, &photoOutput_);
+ if (!photoOutput_ || ret != CAMERA_OK) {
+ qWarning() << "CreatePhotoOutput failed.";
+ return false;
+ }
+
+ ret = OH_PhotoOutput_RegisterPhotoAvailableCallback(photoOutput_, \
+ [](Camera_PhotoOutput* photoOutput, OH_PhotoNative* photo) {
+ if(p_cameraSession)
+ p_cameraSession->onPhotoCallback(photoOutput, photo);
+ });
+
+ return true;
+}
+
+Camera_OutputCapability *QOhCameraSession::cameraAbility()
+{
+ return cameraOutputCapability_;
+}
+
+void QOhCameraSession::presentFrame()
+{
+ m_presentMutex.lock();
+
+ if (m_currentFrame.isValid() && m_surface) {
+ m_surface->present(m_currentFrame);
+ m_currentFrame = QVideoFrame();
+ }
+
+ m_presentMutex.unlock();
+}
+
+void QOhCameraSession::onApplicationStateChanged(Qt::ApplicationState state)
+{
+#if 0
+ switch (state) {
+ case Qt::ApplicationInactive:
+ if (m_state != QCamera::UnloadedState) {
+ m_savedState = m_state;
+ //close(); TODO
+ m_state = QCamera::UnloadedState;
+ emit stateChanged(m_state);
+ }
+ break;
+ case Qt::ApplicationActive:
+ if (m_savedState != -1) {
+ setStateHelper(QCamera::State(m_savedState));
+ m_savedState = -1;
+ }
+ break;
+ default:
+ break;
+ }
+#endif
+}
+
+void QOhCameraSession::updateAvailableCameras()
+{
+ static QElapsedTimer timer;
+ if (timer.isValid() && timer.elapsed() < 500) // ms
+ return;
+
+ deviceList->clear();
+
+ if(!g_cameraManager && OH_Camera_GetCameraManager(&g_cameraManager) != CAMERA_OK)
+ {
+ qWarning() << "OH_Camera_GetCameraManager failed!";
+ return;
+ }
+
+ if(g_cameras)
+ {
+ OH_CameraManager_DeleteSupportedCameras(g_cameraManager, g_cameras, g_camerasCount);
+ g_cameras = nullptr;
+ }
+
+ Camera_ErrorCode ret;
+ ret = OH_CameraManager_GetSupportedCameras(g_cameraManager, &g_cameras, &g_camerasCount);
+ if (g_cameras == nullptr || g_camerasCount == 0 || ret != CAMERA_OK)
+ {
+ qWarning() << "OH_CameraManager_GetSupportedCameras failed or no decices!";
+ return;
+ }
+
+ for(uint32_t i = 0; i < g_camerasCount; ++i)
+ {
+ OhCameraInfo info;
+ info.name = g_cameras[i].cameraId;
+
+ switch(g_cameras[i].connectionType)
+ {
+ case CAMERA_CONNECTION_BUILT_IN: info.description += "Built-in";break;
+ case CAMERA_CONNECTION_USB_PLUGIN: info.description += "using USB";break;
+ case CAMERA_CONNECTION_REMOTE: info.description += "Remote camera";break;
+ default:break;
+ }
+
+ info.description += "-";
+
+ switch(g_cameras[i].cameraType)
+ {
+ case CAMERA_TYPE_DEFAULT: info.description += "Default camera";break;
+ case CAMERA_TYPE_WIDE_ANGLE: info.description += "Wide camera";break;
+ case CAMERA_TYPE_ULTRA_WIDE: info.description += "Ultra wide camera";break;
+ case CAMERA_TYPE_TELEPHOTO: info.description += "Telephoto camera";break;
+ case CAMERA_TYPE_TRUE_DEPTH: info.description += "True depth camera";break;
+ default:break;
+ }
+
+ switch(g_cameras[i].cameraPosition)
+ {
+ case CAMERA_POSITION_UNSPECIFIED: info.position = QCamera::UnspecifiedPosition;break;
+ case CAMERA_POSITION_BACK: info.position = QCamera::BackFace;break;
+ case CAMERA_POSITION_FRONT: info.position = QCamera::FrontFace;break;
+ default:break;
+ }
+
+ info.orientation = 0;
+ deviceList->append(info);
+ }
+
+ timer.restart();
+}
+
+bool QOhCameraSession::load()
+{
+ if (!createPreviewOutput())
+ return false;
+
+ if (m_captureMode.testFlag(QCamera::CaptureStillImage)) {
+ if (!createPhotoOutput())
+ return false;
+ }
+
+ Camera_ErrorCode ret;
+ ret = OH_CaptureSession_BeginConfig(captureSession_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_BeginConfig failed.";
+ return false;
+ }
+
+ if (!cameraInput_) {
+ ret = OH_CameraManager_CreateCameraInput(g_cameraManager, &g_cameras[m_deviceIndex], &cameraInput_);
+ if (!cameraInput_ || ret != CAMERA_OK) {
+ qWarning() << "CreateCameraInput failed.";
+ return false;
+ }
+
+ ret = OH_CameraInput_Open(cameraInput_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "CameraInput_Open failed.";
+ return false;
+ }
+
+ ret = OH_CaptureSession_AddInput(captureSession_, cameraInput_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_AddInput failed.";
+ return false;
+ }
+ }
+
+ if (previewOutput_) {
+ ret = OH_CaptureSession_AddPreviewOutput(captureSession_, previewOutput_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_AddPreviewOutput failed.";
+ return false;
+ }
+ }
+
+ if (photoOutput_) {
+ ret = OH_CaptureSession_AddPhotoOutput(captureSession_, photoOutput_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_AddPhotoOutput failed.";
+ return false;
+ }
+ }
+
+ ret = OH_CaptureSession_CommitConfig(captureSession_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_CommitConfig failed.";
+ unload();
+ return false;
+ }
+
+ m_state = QCamera::LoadedState;
+ emit loaded();
+ return true;
+}
+
+bool QOhCameraSession::unload()
+{
+ if(captureSession_ && (previewOutput_ || cameraInput_ || photoOutput_)){
+ OH_CaptureSession_BeginConfig(captureSession_);
+ if(previewOutput_)
+ OH_CaptureSession_RemovePreviewOutput(captureSession_, previewOutput_);
+ if(photoOutput_)
+ OH_CaptureSession_RemovePhotoOutput(captureSession_, photoOutput_);
+ if(cameraInput_)
+ OH_CaptureSession_RemoveInput(captureSession_, cameraInput_);
+ OH_CaptureSession_CommitConfig(captureSession_);
+ }
+
+ profile_ = nullptr;
+ if (previewOutput_) {
+ OH_PreviewOutput_Stop(previewOutput_);
+ OH_PreviewOutput_Release(previewOutput_);
+ previewOutput_ = nullptr;
+ }
+
+ photoProfile_ = nullptr;
+ if (photoOutput_) {
+ OH_PhotoOutput_Release(photoOutput_);
+ photoOutput_ = nullptr;
+ }
+
+ if (cameraInput_) {
+ OH_CameraInput_Close(cameraInput_);
+ OH_CameraInput_Release(cameraInput_);
+ cameraInput_ = nullptr;
+ }
+
+ stopReceiver();
+
+ m_readyForCapture = false;
+ m_currentImageCaptureId = -1;
+ m_currentImageCaptureFileName.clear();
+ m_state = QCamera::UnloadedState;
+ return true;
+}
+
+bool QOhCameraSession::startPreview()
+{
+ if(!captureSession_)
+ {
+ qWarning() << "captureSession is empty!";
+ return false;
+ }
+
+ Camera_ErrorCode ret;
+
+ ret = OH_CaptureSession_Start(captureSession_);
+ if (ret != CAMERA_OK) {
+ qWarning() << "OH_CaptureSession_Start failed.";
+ return false;
+ }
+ m_sessionStarted = true;
+
+ // ret = OH_PreviewOutput_Start(previewOutput_);
+ // if (ret != CAMERA_OK) {
+ // qWarning() << "OH_PreviewOutput_Start failed.";
+ // return false;
+ // }
+
+ QVideoSurfaceFormat fomat(QSize(profile_->size.width,profile_->size.height),
+ QVideoFrame::Format_ARGB32,
+ QAbstractVideoBuffer::NoHandle);
+ const bool isFront = (m_deviceIndex >= 0
+ && (uint32_t)m_deviceIndex < g_camerasCount
+ && g_cameras[m_deviceIndex].cameraPosition == CAMERA_POSITION_FRONT);
+ fomat.setProperty("mirrored", isFront);
+
+ if (m_surface)
+ m_surface->start(fomat);
+
+ setReadyForCapture(true);
+
+ return true;
+}
+
+bool QOhCameraSession::stopPreview()
+{
+ setReadyForCapture(false);
+
+ if(captureSession_)
+ {
+ // OH_PreviewOutput_Stop(previewOutput_);
+ OH_CaptureSession_Stop(captureSession_);
+ m_sessionStarted = false;
+ return true;
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,154 @@
+#ifndef QOHCAMERASESSION_H
+#define QOHCAMERASESSION_H
+
+#include <QObject>
+#include <QMutex>
+#include <QTimer>
+#include <qcamera.h>
+
+#include "ohcamera.h"
+#include <QCameraImageCapture>
+#include <private/qmediastoragelocation_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct Camera_PreviewOutput;
+struct Camera_VideoOutput;
+struct Camera_Input;
+struct Camera_Manager;
+struct Camera_CaptureSession;
+struct Camera_Device;
+struct Camera_OutputCapability;
+struct Camera_Profile;
+struct Camera_VideoProfile;
+struct OH_ImageReceiverNative;
+struct OH_ImageReceiverOptions;
+struct OH_ImageReceiverNative;
+struct Camera_PhotoOutput;
+struct OH_PhotoNative;
+
+class QOhCameraSession : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QOhCameraSession(QObject *parent = nullptr);
+ ~QOhCameraSession();
+ static const QList<OhCameraInfo> &availableCameras();
+
+ QCamera::State state() const { return m_state; }
+ void setState(QCamera::State state);
+
+ QCamera::Status status() const { return m_status; }
+
+ void setDevice(const QString &device);
+
+ QCamera::CaptureModes captureMode() const { return m_captureMode; }
+ void setCaptureMode(QCamera::CaptureModes mode);
+ bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
+
+ bool isReadyForCapture();
+ int capture(const QString &fileName);
+ void cancelCapture();
+ QCameraImageCapture::DriveMode driveMode() const;
+ void setDriveMode(QCameraImageCapture::DriveMode mode);
+ void setReadyForCapture(bool ready);
+
+ QCameraImageCapture::CaptureDestinations captureDestination() const;
+ void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination);
+
+ QCameraViewfinderSettings viewfinderSettings() const;
+ void setViewfinderSettings(const QCameraViewfinderSettings &settings);
+
+ void setSurface(QAbstractVideoSurface* surface);
+ void onReceiverCallback(OH_ImageReceiverNative *receiver);
+ void onPhotoCallback(Camera_PhotoOutput* photoOutput, OH_PhotoNative* photo);
+
+ bool addVideoOutput(const QString &surfaceID, const QSize &reslotion);
+ bool removeVideoOutput();
+
+ Camera_OutputCapability *cameraAbility();
+ QCameraViewfinderSettings profileToSetting(Camera_Profile *profile);
+Q_SIGNALS:
+ void statusChanged(QCamera::Status status);
+ void stateChanged(QCamera::State);
+ void error(int error, const QString &errorString);
+ void captureModeChanged(QCamera::CaptureModes);
+ void loaded();
+
+ void captureDestinationChanged(QCameraImageCapture::CaptureDestinations destination);
+
+ void readyForCaptureChanged(bool);
+ void imageExposed(int id);
+ void imageCaptured(int id, const QImage &preview);
+ void imageMetadataAvailable(int id, const QString &key, const QVariant &value);
+ void imageAvailable(int id, const QVideoFrame &buffer);
+ void imageSaved(int id, const QString &fileName);
+ void imageCaptureError(int id, int error, const QString &errorString);
+
+private Q_SLOTS:
+ void presentFrame();
+ void onApplicationStateChanged(Qt::ApplicationState state);
+
+private:
+ static void updateAvailableCameras();
+ bool load();
+ bool unload();
+ bool startPreview();
+ bool stopPreview();
+
+ void setStateHelper(QCamera::State state);
+
+ void initCameraSession();
+ bool createPreviewOutput();
+ bool createPhotoOutput();
+
+ bool initReceiver(uint32_t width, uint32_t height);
+ void stopReceiver();
+
+ void processCapturedImage(int id,
+ const QVideoFrame &data,
+ QCameraImageCapture::CaptureDestinations dest,
+ const QString &fileName);
+
+ QString m_sourceDeviceName = QLatin1String("default");
+ char m_surfaceID[21];
+ QAbstractVideoSurface *m_surface = nullptr;
+ QCamera::State m_state;
+ int m_savedState;
+ QCamera::Status m_status;
+ QCamera::CaptureModes m_captureMode;
+ bool m_captureCanceled;
+ int m_lastImageCaptureId;
+ QCameraImageCapture::CaptureDestinations m_captureDestination;
+ QCameraImageCapture::DriveMode m_captureImageDriveMode;
+ bool m_readyForCapture;
+ int m_currentImageCaptureId;
+ QString m_currentImageCaptureFileName;
+
+ QMediaStorageLocation m_mediaStorageLocation;
+
+ QMutex m_presentMutex;
+ QVideoFrame m_currentFrame;
+
+ QCameraViewfinderSettings m_viewfinderSettings;
+ QCameraViewfinderSettings m_actualViewfinderSettings;
+
+ Camera_PreviewOutput *previewOutput_ = nullptr;
+ Camera_PhotoOutput* photoOutput_ = nullptr;
+ Camera_VideoOutput *videoOutput_ = nullptr;
+ static Camera_Manager *g_cameraManager;
+ static Camera_Device *g_cameras;
+ static uint32_t g_camerasCount;
+ Camera_Input *cameraInput_ = nullptr;
+ int m_deviceIndex = 0;
+ Camera_CaptureSession *captureSession_ = nullptr;
+ bool m_sessionStarted = false;
+ Camera_OutputCapability *cameraOutputCapability_ = nullptr;
+ Camera_Profile *profile_ = nullptr;
+ Camera_Profile *photoProfile_ = nullptr;
+ OH_ImageReceiverOptions *m_recOptions = nullptr;
+ OH_ImageReceiverNative *m_receiver = nullptr;
+};
+
+QT_END_NAMESPACE
+#endif // QOHCAMERASESSION_H
new file mode 100644
@@ -0,0 +1,97 @@
+#include "qohcamerasession.h"
+#include "qohcapturesession.h"
+#include "qohcameracontrol.h"
+#include "qohcaptureservice.h"
+#include "qmediaserviceproviderplugin.h"
+#include "qohcamerainfocontrol.h"
+#include "qohcamerarendercontrol.h"
+#include "qohcameraimagecapturecontrol.h"
+#include "qohdevicecontrol.h"
+#include "qohrecordercontrol.h"
+#include "qohaudioencodercontrol.h"
+#include "qohcontainercontrol.h"
+#include "qohvideoencodercontrol.h"
+#include "qohviewfindersettingscontrol2.h"
+
+QOhCaptureService::QOhCaptureService(QObject *parent)
+ : QMediaService{ parent }
+{
+ m_cameraSession = new QOhCameraSession();
+ m_cameraControl = new QOhCameraControl(m_cameraSession);
+ m_cameraInfoControl = new QOhCameraInfoControl();
+ m_videoDevice = new QOhDeviceControl(m_cameraSession);
+ m_viewfinderSettings = new QOhViewfinderSettingsControl2(m_cameraSession);
+
+ m_captureSession = new QOhCaptureSession(m_cameraSession);
+ m_recorderControl = new QOhRecorderControl(m_captureSession);
+ m_audioEncoderSettingsControl = new QOhAudioEncoderControl(m_captureSession);
+ m_mediaContainerControl = new QOhContainerControl(m_captureSession);
+ m_imageCaptureControl = new QOhCameraImageCaptureControl(m_cameraSession);
+ m_videoEncoderSettingsControl = new QOhVideoEncoderControl(m_captureSession);
+}
+
+QOhCaptureService::~QOhCaptureService()
+{
+ delete m_videoEncoderSettingsControl;
+ delete m_imageCaptureControl;
+ delete m_mediaContainerControl;
+ delete m_audioEncoderSettingsControl;
+ delete m_recorderControl;
+ delete m_captureSession;
+
+ delete m_viewfinderSettings;
+ delete m_videoDevice;
+ delete m_cameraInfoControl;
+ delete m_cameraControl;
+ delete m_cameraSession;
+}
+
+QMediaControl *QOhCaptureService::requestControl(const char *name)
+{
+ if (qstrcmp(name, QMediaRecorderControl_iid) == 0)
+ return m_recorderControl;
+
+ if (qstrcmp(name, QMediaContainerControl_iid) == 0)
+ return m_mediaContainerControl;
+
+ if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
+ return m_imageCaptureControl;
+
+ if (qstrcmp(name, QAudioEncoderSettingsControl_iid) == 0)
+ return m_audioEncoderSettingsControl;
+
+ if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
+ return m_videoEncoderSettingsControl;
+
+ if(qstrcmp(name,QCameraControl_iid) == 0)
+ return m_cameraControl;
+
+ if (qstrcmp(name, QCameraInfoControl_iid) == 0)
+ return m_cameraInfoControl;
+
+ if (qstrcmp(name,QVideoRendererControl_iid) == 0) {
+ if (!m_videoRenderer) {
+ m_videoRenderer = new QOhCameraRenderControl(m_cameraSession, this);
+ return m_videoRenderer;
+ }
+ }
+
+ if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0)
+ return m_videoDevice;
+
+ if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0)
+ return m_viewfinderSettings;
+ return nullptr;
+}
+
+void QOhCaptureService::releaseControl(QMediaControl *control)
+{
+ if(control){
+ if(control == m_videoRenderer)
+ {
+ delete m_videoRenderer;
+ m_videoRenderer = 0;
+ return;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,52 @@
+#ifndef QOHCAPTURESERVICE_H
+#define QOHCAPTURESERVICE_H
+
+#include <qmediaservice.h>
+#include <qmediacontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCameraControl;
+class QOhCameraSession;
+class QOhCameraInfoControl;
+class QOhCameraRenderControl;
+class QOhCameraImageCaptureControl;
+class QOhDeviceControl;
+class QOhCaptureSession;
+class QOhRecorderControl;
+class QOhAudioEncoderControl;
+class QOhContainerControl;
+class QOhVideoEncoderControl;
+class QOhViewfinderSettingsControl2;
+
+class QOhCaptureService : public QMediaService
+{
+ Q_OBJECT
+
+public:
+ explicit QOhCaptureService(QObject *parent = nullptr);
+ virtual ~QOhCaptureService();
+
+ QMediaControl *requestControl(const char *name);
+ void releaseControl(QMediaControl *);
+
+private:
+ QString m_service;
+
+ QOhCameraSession *m_cameraSession = nullptr;
+ QOhCameraControl *m_cameraControl = nullptr;
+ QOhCameraInfoControl *m_cameraInfoControl = nullptr;
+ QOhCameraRenderControl *m_videoRenderer = nullptr;
+ QOhCameraImageCaptureControl *m_imageCaptureControl = nullptr;
+ QOhDeviceControl *m_videoDevice = nullptr;
+ QOhCaptureSession *m_captureSession = nullptr;
+ QOhRecorderControl *m_recorderControl = nullptr;
+ QOhAudioEncoderControl *m_audioEncoderSettingsControl = nullptr;
+ QOhContainerControl *m_mediaContainerControl = nullptr;
+ QOhVideoEncoderControl *m_videoEncoderSettingsControl = nullptr;
+ QOhViewfinderSettingsControl2 *m_viewfinderSettings = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHCAPTURESERVICE_H
new file mode 100644
@@ -0,0 +1,449 @@
+#include "qohcapturesession.h"
+#include "qohcamerasession.h"
+#include "ohmultimediautils.h"
+
+#include "ohcamera/camera.h"
+QT_BEGIN_NAMESPACE
+
+QOhCaptureSession::QOhCaptureSession(QOhCameraSession *cameraSession)
+ : QObject()
+ , m_mediaRecorder(0)
+ , m_cameraSession(cameraSession)
+ , m_duration(0)
+ , m_state(QMediaRecorder::StoppedState)
+ , m_status(QMediaRecorder::UnloadedStatus)
+ , m_containerFormat()
+ , m_containerFormatDirty(true)
+ , m_videoSettingsDirty(true)
+ , m_audioSettingsDirty(true)
+{
+ m_mediaStorageLocation.addStorageLocation(
+ QMediaStorageLocation::Movies,
+ OhMultimediaUtils::getDefaultMediaDirectory(OhMultimediaUtils::Video));
+
+ if(cameraSession) {
+ // onCameraLoaded();
+ connect(cameraSession, SIGNAL(loaded()), this, SLOT(onCameraLoaded()));
+ connect(cameraSession, &QOhCameraSession::statusChanged, this,
+ [this](QCamera::Status status) {
+ if (status == QCamera::UnavailableStatus) {
+ setState(QMediaRecorder::StoppedState);
+ setStatus(QMediaRecorder::UnavailableStatus);
+ return;
+ }
+
+ // Stop recording when stopping the camera.
+ if (status == QCamera::StoppingStatus) {
+ setState(QMediaRecorder::StoppedState);
+ setStatus(QMediaRecorder::UnloadedStatus);
+ return;
+ }
+
+ if (status == QCamera::LoadingStatus)
+ setStatus(QMediaRecorder::LoadingStatus);
+ });
+ connect(cameraSession, &QOhCameraSession::captureModeChanged, this,
+ [this](QCamera::CaptureModes mode) {
+ if (!mode.testFlag(QCamera::CaptureVideo)) {
+ setState(QMediaRecorder::StoppedState);
+ setStatus(QMediaRecorder::UnloadedStatus);
+ }
+ });
+ setStatus(QMediaRecorder::LoadedStatus);
+ // connect(cameraSession, &QOhCameraSession::readyForCaptureChanged, this,
+ // [this](bool ready) {
+ // if (ready)
+ // setStatus(QMediaRecorder::LoadedStatus);
+ // });
+ }
+ else{
+ // Audio-only recording.
+ setStatus(QMediaRecorder::LoadedStatus);
+ }
+
+ m_notifyTimer.setInterval(1000);
+ connect(&m_notifyTimer, SIGNAL(timeout()), this, SLOT(updateDuration()));
+}
+
+QOhCaptureSession::~QOhCaptureSession()
+{
+ stop();
+ if(m_mediaRecorder)
+ {
+ delete m_mediaRecorder;
+ }
+}
+
+QUrl QOhCaptureSession::outputLocation() const
+{
+ if(m_actualOutputLocation.isEmpty())
+ {
+ return m_requestedOutputLocation;
+ }
+ return m_actualOutputLocation;
+}
+
+bool QOhCaptureSession::setOutputLocation(const QUrl &location)
+{
+ if (m_requestedOutputLocation == location)
+ return false;
+
+ m_actualOutputLocation = QUrl();
+ m_requestedOutputLocation = location;
+
+ if (m_requestedOutputLocation.isEmpty())
+ return true;
+
+ if (m_requestedOutputLocation.isValid()
+ && (m_requestedOutputLocation.isLocalFile() || m_requestedOutputLocation.isRelative())) {
+ return true;
+ }
+
+ m_requestedOutputLocation = QUrl();
+ return false;
+}
+
+qint64 QOhCaptureSession::duration() const
+{
+ return m_duration;
+}
+
+QMediaRecorder::State QOhCaptureSession::state() const
+{
+ return m_state;
+}
+
+QMediaRecorder::Status QOhCaptureSession::status() const
+{
+ return m_status;
+}
+
+void QOhCaptureSession::setState(QMediaRecorder::State state)
+{
+ if (m_state == state)
+ return;
+
+ switch (state) {
+ case QMediaRecorder::StoppedState:
+ stop();
+ break;
+ case QMediaRecorder::RecordingState:
+ start();
+ break;
+ case QMediaRecorder::PausedState:
+ paused();
+ break;
+ }
+}
+
+void QOhCaptureSession::setContainerFormat(const QString &format)
+{
+ if (m_containerFormat == format)
+ return;
+
+ m_containerFormat = format;
+ m_containerFormatDirty = true;
+}
+
+void QOhCaptureSession::setAudioSettings(const QAudioEncoderSettings &settings)
+{
+ if (m_audioSettings == settings)
+ return;
+
+ m_audioSettings = settings;
+ m_audioSettingsDirty = true;
+}
+
+void QOhCaptureSession::setVideoSettings(const QVideoEncoderSettings &settings)
+{
+ if (!m_cameraSession || m_videoSettings == settings)
+ return;
+
+ m_videoSettings = settings;
+ m_videoSettingsDirty = true;
+}
+
+void QOhCaptureSession::applySettings()
+{
+ // container settings
+ if (m_containerFormatDirty) {
+ //only support mp4
+ m_containerFormat = QStringLiteral("mp4");
+ m_containerFormatDirty = false;
+ }
+
+ // audio settings
+ if (m_audioSettingsDirty) {
+ if (m_audioSettings.channelCount() <= 0)
+ m_audioSettings.setChannelCount(1);
+ if (m_audioSettings.bitRate() <= 0)
+ m_audioSettings.setBitRate(48000);
+ if (m_audioSettings.sampleRate() <= 0)
+ m_audioSettings.setSampleRate(48000);
+
+ if (m_audioSettings.codec().isEmpty())
+ m_audioEncoder = OhMediaRecorder::AAC;
+ else if (m_audioSettings.codec() == QLatin1String("audio/mp4a-latm"))
+ m_audioEncoder = OhMediaRecorder::AAC;
+ else if (m_audioSettings.codec() == QLatin1String("audio/g711mu"))
+ m_audioEncoder = OhMediaRecorder::G711MU;
+ else
+ m_audioEncoder = OhMediaRecorder::AAC;
+
+ m_audioSettingsDirty = false;
+ }
+
+ // video settings
+ if (m_cameraSession && m_videoSettingsDirty) {
+ if (m_videoSettings.resolution().isEmpty()) {
+ m_videoSettings.setResolution(m_supportedResolutions.back());
+ } else if (!m_supportedResolutions.contains(m_videoSettings.resolution())) {
+ // if the requested resolution is not supported, find the closest one
+ QSize reqSize = m_videoSettings.resolution();
+ int reqPixelCount = reqSize.width() * reqSize.height();
+ QList<int> supportedPixelCounts;
+ for (int i = 0; i < m_supportedResolutions.size(); ++i) {
+ const QSize &s = m_supportedResolutions.at(i);
+ supportedPixelCounts.append(s.width() * s.height());
+ }
+ int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
+ m_videoSettings.setResolution(m_supportedResolutions.at(closestIndex));
+ }
+
+ if (m_supportedFramerates.isEmpty() && \
+ (m_videoSettings.frameRate() <= 0 || m_videoSettings.frameRate() >= m_supportedFramerates.back()) )
+ m_videoSettings.setFrameRate(m_supportedFramerates.back());
+
+ if (m_videoSettings.bitRate() <= 0)
+ m_videoSettings.setBitRate(200000);
+
+ m_videoEncoder = OhMediaRecorder::AVC;
+
+ m_videoSettingsDirty = false;
+ }
+}
+
+void QOhCaptureSession::updateDuration()
+{
+ if (m_elapsedTime.isValid())
+ m_duration = m_elapsedTime.elapsed();
+
+ emit durationChanged(m_duration);
+}
+
+void QOhCaptureSession::onCameraLoaded()
+{
+ Camera_OutputCapability *ability = m_cameraSession->cameraAbility();
+ if(!ability)
+ {
+ return;
+ }
+
+ m_supportedResolutions.clear();
+ m_supportedFramerates.clear();
+
+ QList<int> defaultRates({5, 10, 15, 30, 45, 60});
+
+ for(int i = 0; i < ability->videoProfilesSize; ++i)
+ {
+ Camera_VideoProfile *profile = ability->videoProfiles[i];
+ if(profile->size.width <= 4096 && profile->size.width >= 176 &&\
+ profile->size.height <= 4096 && profile->size.height >= 144){
+ m_supportedResolutions.append(QSize(profile->size.width, profile->size.height));
+ }
+
+ for(int &rate : defaultRates)
+ {
+ if(rate && rate >= profile->range.min && rate <= profile->range.max)
+ {
+ m_supportedFramerates.append(rate);
+ rate = 0;
+ }
+ }
+ }
+
+ m_supportedResolutions = QSet<QSize>(m_supportedResolutions.begin(), m_supportedResolutions.end()).values();
+ std::sort(m_supportedResolutions.begin(), m_supportedResolutions.end(), qt_sizeLessThan);
+ std::sort(m_supportedFramerates.begin(), m_supportedFramerates.end());
+
+ applySettings();
+}
+
+void QOhCaptureSession::onError(int what, int extra)
+{
+ Q_UNUSED(what)
+ Q_UNUSED(extra)
+ stop(true);
+ emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error."));
+}
+
+void QOhCaptureSession::onInfo(int what, int extra)
+{
+ Q_UNUSED(extra)
+ if (what == 800) {
+ // MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
+ setState(QMediaRecorder::StoppedState);
+ emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached."));
+ } else if (what == 801) {
+ // MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
+ setState(QMediaRecorder::StoppedState);
+ emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum file size reached."));
+ }
+}
+
+void QOhCaptureSession::start()
+{
+ if (m_state == QMediaRecorder::PausedState && m_mediaRecorder)
+ {
+ m_mediaRecorder->resume();
+ setStatus(QMediaRecorder::RecordingStatus);
+ m_state = QMediaRecorder::RecordingState;
+ return;
+ }
+
+ if (m_state == QMediaRecorder::RecordingState || m_status != QMediaRecorder::LoadedStatus)
+ return;
+
+ setStatus(QMediaRecorder::StartingStatus);
+
+ if (m_mediaRecorder) {
+ m_mediaRecorder->release();
+ delete m_mediaRecorder;
+ }
+
+ const bool granted = OhMultimediaUtils::qt_openharmonyRequestPermission();
+ if (!granted) {
+ setStatus(QMediaRecorder::UnavailableStatus);
+ Q_EMIT error(QMediaRecorder::ResourceError, QLatin1String("Permission denied."));
+ return;
+ }
+
+ m_mediaRecorder = new OhMediaRecorder;
+ connect(m_mediaRecorder, SIGNAL(error(int,int)), this, SLOT(onError(int,int)));
+ connect(m_mediaRecorder, SIGNAL(info(int,int)), this, SLOT(onInfo(int,int)));
+
+ // Set audio encoder settings
+ m_mediaRecorder->setAudioChannels(m_audioSettings.channelCount());
+ m_mediaRecorder->setAudioEncodingBitRate(m_audioSettings.bitRate());
+ m_mediaRecorder->setAudioSamplingRate(m_audioSettings.sampleRate());
+ m_mediaRecorder->setAudioEncoder(m_audioEncoder);
+
+ // Set video encoder settings
+ if (m_cameraSession) {
+ m_mediaRecorder->setvideoFramSize(m_videoSettings.resolution());
+ m_mediaRecorder->setvideoFrameRate(qRound(m_videoSettings.frameRate()));
+ m_mediaRecorder->setVideoBitrate(m_videoSettings.bitRate());
+ m_mediaRecorder->setVideoEncoder(m_videoEncoder);
+
+ // m_mediaRecorder->setOrientationHint(m_cameraSession->currentCameraRotation());
+ }
+
+ // Set output file
+ QString filePath = m_mediaStorageLocation.generateFileName(
+ m_requestedOutputLocation.isLocalFile() ? m_requestedOutputLocation.toLocalFile()
+ : m_requestedOutputLocation.toString(),
+ QMediaStorageLocation::Movies,
+ QLatin1String("VID_"),
+ m_containerFormat);
+
+ m_usedOutputLocation = QUrl::fromLocalFile(filePath);
+ m_mediaRecorder->setOutputFile(filePath);
+
+ m_mediaRecorder->reset();
+
+ QString videoSurface = m_mediaRecorder->prepare();
+ if (videoSurface.isEmpty()) {
+ emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder and get surface."));
+ return;
+ }
+
+ if(!m_cameraSession || !m_cameraSession->addVideoOutput(videoSurface, m_videoSettings.resolution()))
+ {
+ emit error(QMediaRecorder::FormatError, QLatin1String("Unable to create videoOutput."));
+ return;
+ }
+
+ if (!m_mediaRecorder->start()) {
+ emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder."));
+ return;
+ }
+
+ m_elapsedTime.start();
+ m_notifyTimer.start();
+ updateDuration();
+
+ if(m_state != QMediaRecorder::RecordingState)
+ {
+ m_state = QMediaRecorder::RecordingState;
+ emit stateChanged(m_state);
+ }
+ if(m_status != QMediaRecorder::RecordingStatus)
+ {
+ m_status = QMediaRecorder::RecordingStatus;
+ emit stateChanged(m_state);
+ }
+
+ m_actualOutputLocation = m_usedOutputLocation;
+ emit actualLocationChanged(m_actualOutputLocation);
+}
+
+void QOhCaptureSession::stop(bool error)
+{
+ if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder == 0)
+ return;
+
+ setStatus(QMediaRecorder::FinalizingStatus);
+
+ m_mediaRecorder->stop();
+ m_notifyTimer.stop();
+ updateDuration();
+ m_elapsedTime.invalidate();
+ m_mediaRecorder->release();
+ delete m_mediaRecorder;
+ m_mediaRecorder = 0;
+
+ if(m_cameraSession)
+ m_cameraSession->removeVideoOutput();
+
+ if (!error) {
+ m_actualOutputLocation = m_usedOutputLocation;
+ emit actualLocationChanged(m_actualOutputLocation);
+ }
+
+ m_state = QMediaRecorder::StoppedState;
+ emit stateChanged(m_state);
+ if (!m_cameraSession)
+ setStatus(QMediaRecorder::LoadedStatus);
+}
+
+void QOhCaptureSession::paused()
+{
+ if (m_state != QMediaRecorder::RecordingState || m_mediaRecorder == 0)
+ return;
+
+ m_mediaRecorder->pause();
+
+ m_state = QMediaRecorder::PausedState;
+ setStatus(QMediaRecorder::PausedStatus);
+}
+
+void QOhCaptureSession::setStatus(QMediaRecorder::Status status)
+{
+ if (m_status == status)
+ return;
+
+ m_status = status;
+ emit statusChanged(m_status);
+}
+
+void QOhCaptureSession::updateViewfinder()
+{
+
+}
+
+void QOhCaptureSession::restartViewfinder()
+{
+
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,102 @@
+#ifndef QOHCAPTURESESSION_H
+#define QOHCAPTURESESSION_H
+
+#include <qurl.h>
+#include <qtimer.h>
+#include <qobject.h>
+#include <qelapsedtimer.h>
+#include <qmediarecorder.h>
+#include "ohmediarecorder.h"
+#include <private/qmediastoragelocation_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCameraSession;
+struct Camera_OutputCapability;
+
+class QOhCaptureSession : public QObject
+{
+ Q_OBJECT
+
+public:
+ QOhCaptureSession(QOhCameraSession *cameraSession = Q_NULLPTR);
+ ~QOhCaptureSession();
+
+ QList<QSize> supportedResolutions() const { return m_supportedResolutions; }
+ QList<qreal> supportedFrameRates() const { return m_supportedFramerates; }
+
+ QUrl outputLocation() const;
+ bool setOutputLocation(const QUrl &location);
+
+ qint64 duration() const;
+ QMediaRecorder::State state() const;
+ QMediaRecorder::Status status() const;
+ void setState(QMediaRecorder::State state);
+
+ QString containerFormat() const { return m_containerFormat; }
+ void setContainerFormat(const QString &format);
+
+ QAudioEncoderSettings audioSettings() const { return m_audioSettings; }
+ void setAudioSettings(const QAudioEncoderSettings &settings);
+
+ QVideoEncoderSettings videoSettings() const { return m_videoSettings; }
+ void setVideoSettings(const QVideoEncoderSettings &settings);
+
+ void applySettings();
+Q_SIGNALS:
+ void durationChanged(qint64 position);
+ void audioInputChanged(const QString& name);
+ void stateChanged(QMediaRecorder::State state);
+ void actualLocationChanged(const QUrl &location);
+ void statusChanged(QMediaRecorder::Status status);
+ void error(int error, const QString &errorString);
+
+private Q_SLOTS:
+ void updateDuration();
+ void onCameraLoaded();
+
+ void onError(int what, int extra);
+ void onInfo(int what, int extra);
+
+private:
+ void start();
+ void stop(bool error = false);
+ void paused();
+
+ void setStatus(QMediaRecorder::Status status);
+
+ void updateViewfinder();
+ void restartViewfinder();
+
+ OhMediaRecorder *m_mediaRecorder;
+ QOhCameraSession *m_cameraSession;
+
+ QMediaStorageLocation m_mediaStorageLocation;
+
+ QElapsedTimer m_elapsedTime;
+ QTimer m_notifyTimer;
+ qint64 m_duration;
+
+ QMediaRecorder::State m_state;
+ QMediaRecorder::Status m_status;
+ QUrl m_requestedOutputLocation;
+ QUrl m_usedOutputLocation;
+ QUrl m_actualOutputLocation;
+
+ QString m_containerFormat;
+ QAudioEncoderSettings m_audioSettings;
+ QVideoEncoderSettings m_videoSettings;
+ bool m_containerFormatDirty;
+ bool m_videoSettingsDirty;
+ bool m_audioSettingsDirty;
+ OhMediaRecorder::AudioEncoder m_audioEncoder;
+ OhMediaRecorder::VideoEncoder m_videoEncoder;
+
+ QList<QSize> m_supportedResolutions;
+ QList<qreal> m_supportedFramerates;
+
+};
+
+
+QT_END_NAMESPACE
+#endif // QOHCAPTURESESSION_H
new file mode 100644
@@ -0,0 +1,43 @@
+#include "qohcapturesession.h"
+#include "qohcontainercontrol.h"
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+QOhContainerControl::QOhContainerControl(QOhCaptureSession *session)
+ : QMediaContainerControl()
+ , m_session(session)
+{
+
+}
+
+QString QOhContainerControl::containerFormat() const
+{
+ return m_session->containerFormat();
+}
+
+QStringList QOhContainerControl::supportedContainers() const
+{
+ return QStringList() << QLatin1String("mp4");
+}
+
+void QOhContainerControl::setContainerFormat(const QString &format)
+{
+ if(format != QLatin1String("mp4"))
+ {
+ qWarning() << "Only supported mp4 format!";
+ return;
+ }
+
+ m_session->setContainerFormat(format);
+}
+
+QString QOhContainerControl::containerDescription(const QString &formatMimeType) const
+{
+ if (formatMimeType == QLatin1String("mp4"))
+ return tr("MPEG4 media file format");
+
+ return QString();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,27 @@
+#ifndef QOHCONTAINERCONTROL_H
+#define QOHCONTAINERCONTROL_H
+
+#include <qmediacontainercontrol.h>
+
+QT_BEGIN_NAMESPACE
+class QOhCaptureSession;
+
+class QOhContainerControl : public QMediaContainerControl
+{
+ Q_OBJECT
+
+public:
+ QOhContainerControl(QOhCaptureSession *session);
+
+ QString containerFormat() const override;
+ QStringList supportedContainers() const override;
+ void setContainerFormat(const QString &format) override;
+ QString containerDescription(const QString &formatMimeType) const override;
+
+private:
+ QOhCaptureSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHCONTAINERCONTROL_H
new file mode 100644
@@ -0,0 +1,70 @@
+#include <QDebug>
+#include <qelapsedtimer.h>
+
+#include "qohdevicecontrol.h"
+#include "qohcamerasession.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhDeviceControl::QOhDeviceControl(QObject *parent)
+ : QVideoDeviceSelectorControl(parent)
+{
+ m_session = qobject_cast<QOhCameraSession*>(parent);
+ selected = 0;
+}
+
+QOhDeviceControl::~QOhDeviceControl()
+{
+
+}
+
+int QOhDeviceControl::deviceCount() const
+{
+ const QList<OhCameraInfo> &deviceList = QOhCameraSession::availableCameras();
+ return deviceList.count();
+}
+
+QString QOhDeviceControl::deviceName(int index) const
+{
+ const QList<OhCameraInfo> &deviceList = QOhCameraSession::availableCameras();
+
+ if (index >= 0 && index <= deviceList.count())
+ return QString::fromUtf8(deviceList.at(index).name.constData());
+
+ return QString();
+}
+
+QString QOhDeviceControl::deviceDescription(int index) const
+{
+ const QList<OhCameraInfo> &deviceList = QOhCameraSession::availableCameras();
+
+ if (index >= 0 && index <= deviceList.count())
+ return deviceList.at(index).description;
+
+ return QString();
+}
+
+int QOhDeviceControl::defaultDevice() const
+{
+ return 0;
+}
+
+int QOhDeviceControl::selectedDevice() const
+{
+ return selected;
+}
+
+void QOhDeviceControl::setSelectedDevice(int index)
+{
+ const QList<OhCameraInfo> &deviceList = QOhCameraSession::availableCameras();
+
+ if (index >= 0 && index < deviceList.count()) {
+ if (m_session) {
+ QString device = deviceList.at(index).name;
+ m_session->setDevice(device);
+ }
+ selected = index;
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,32 @@
+#ifndef QOHDEVICECONTROL_H
+#define QOHDEVICECONTROL_H
+
+#include <qvideodeviceselectorcontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCameraSession;
+
+class QOhDeviceControl: public QVideoDeviceSelectorControl
+{
+ Q_OBJECT
+public:
+ QOhDeviceControl(QObject *parent = nullptr);
+ ~QOhDeviceControl();
+
+ int deviceCount() const override;
+ QString deviceName(int index) const override;
+ QString deviceDescription(int index) const override;
+ int defaultDevice() const override;
+ int selectedDevice() const override;
+
+public Q_SLOTS:
+ void setSelectedDevice(int index) override;
+
+private:
+ QOhCameraSession* m_session;
+ int selected;
+};
+
+QT_END_NAMESPACE
+#endif // QOHDEVICECONTROL_H
new file mode 100644
@@ -0,0 +1,70 @@
+#include "qohcapturesession.h"
+#include "qohrecordercontrol.h"
+
+QOhRecorderControl::QOhRecorderControl(QOhCaptureSession *session)
+ :QMediaRecorderControl()
+ , m_session(session)
+{
+ connect(m_session, SIGNAL(stateChanged(QMediaRecorder::State)), this, SIGNAL(stateChanged(QMediaRecorder::State)));
+ connect(m_session, SIGNAL(statusChanged(QMediaRecorder::Status)), this, SIGNAL(statusChanged(QMediaRecorder::Status)));
+ connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
+ connect(m_session, SIGNAL(actualLocationChanged(QUrl)), this, SIGNAL(actualLocationChanged(QUrl)));
+ connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
+}
+
+QUrl QOhRecorderControl::outputLocation() const
+{
+ return m_session->outputLocation();
+}
+
+bool QOhRecorderControl::setOutputLocation(const QUrl &location)
+{
+ return m_session->setOutputLocation(location);
+}
+
+QMediaRecorder::State QOhRecorderControl::state() const
+{
+ return m_session->state();
+}
+
+QMediaRecorder::Status QOhRecorderControl::status() const
+{
+ return m_session->status();
+}
+
+qint64 QOhRecorderControl::duration() const
+{
+ return m_session->duration();
+}
+
+bool QOhRecorderControl::isMuted() const
+{
+ return false;
+}
+
+qreal QOhRecorderControl::volume() const
+{
+ return 1.0f;
+}
+
+void QOhRecorderControl::applySettings()
+{
+ m_session->applySettings();
+}
+
+void QOhRecorderControl::setState(QMediaRecorder::State state)
+{
+ m_session->setState(state);
+}
+
+void QOhRecorderControl::setMuted(bool muted)
+{
+ Q_UNUSED(muted)
+ qWarning("QMediaRecorder::setMuted() is not supported on Openharmony.");
+}
+
+void QOhRecorderControl::setVolume(qreal volume)
+{
+ Q_UNUSED(volume)
+ qWarning("QMediaRecorder::setVolume() is not supported on Openharmony.");
+}
new file mode 100644
@@ -0,0 +1,36 @@
+#ifndef QOHRECORDERCONTROL_H
+#define QOHRECORDERCONTROL_H
+
+#include <qmediarecordercontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCaptureSession;
+
+class QOhRecorderControl : public QMediaRecorderControl
+{
+ Q_OBJECT
+public:
+ explicit QOhRecorderControl(QOhCaptureSession *session);
+
+ QUrl outputLocation() const override;
+ bool setOutputLocation(const QUrl &location) override;
+ QMediaRecorder::State state() const override;
+ QMediaRecorder::Status status() const override;
+ qint64 duration() const override;
+ bool isMuted() const override;
+ qreal volume() const override;
+ void applySettings() override;
+
+public Q_SLOTS:
+ void setState(QMediaRecorder::State state) override;
+ void setMuted(bool muted) override;
+ void setVolume(qreal volume) override;
+
+private:
+ QOhCaptureSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHRECORDERCONTROL_H
new file mode 100644
@@ -0,0 +1,73 @@
+#include "qohcapturesession.h"
+#include "qohvideoencodercontrol.h"
+
+QOhVideoEncoderControl::QOhVideoEncoderControl(QOhCaptureSession *session)
+ : QVideoEncoderSettingsControl()
+ , m_session(session) { }
+
+QList<QSize> QOhVideoEncoderControl::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const
+{
+ if (continuous)
+ *continuous = false;
+
+ return m_session->supportedResolutions();
+}
+
+QList<qreal> QOhVideoEncoderControl::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const
+{
+ if (continuous)
+ *continuous = false;
+
+ return m_session->supportedFrameRates();
+}
+
+QStringList QOhVideoEncoderControl::supportedVideoCodecs() const
+{
+ return QStringList() << QLatin1String("video/avc");
+}
+
+QString QOhVideoEncoderControl::videoCodecDescription(const QString &codecName) const
+{
+ if (codecName == QLatin1String("video/avc"))
+ return tr("Advanced Video Coding (H.264)");
+
+ // if (codecName == QLatin1String("video/h263"))
+ // return tr("H.263 compression");
+
+ // if (codecName == QLatin1String("video/mpeg2"))
+ // return tr("MPEG-2 video codec");
+
+ // if (codecName == QLatin1String("video/mp4v-es"))
+ // return tr("MPEG-4 Visual (MPEG-4 Part 2)");
+
+ // if (codecName == QLatin1String("video/x-vnd.on2.vp8"))
+ // return tr("VP8 video codec");
+
+ return QString();
+}
+
+QVideoEncoderSettings QOhVideoEncoderControl::videoSettings() const
+{
+ return m_session->videoSettings();
+}
+
+void QOhVideoEncoderControl::setVideoSettings(const QVideoEncoderSettings &settings)
+{
+ QVideoEncoderSettings tsettings(settings);
+ if(tsettings.bitRate() == 0)
+ tsettings.setBitRate(200000);
+ else
+ tsettings.setBitRate( qBound(10000, tsettings.bitRate(), 100000000) );
+
+ if(tsettings.frameRate() == 0)
+ tsettings.setFrameRate(30);
+ else
+ tsettings.setFrameRate( qBound(1.0, tsettings.frameRate(), 60.0) );
+
+ QSize newResolution = tsettings.resolution();
+ newResolution.setWidth( qBound(640, newResolution.width(), 4096));
+ newResolution.setHeight( qBound(480, newResolution.height(), 4096));
+ tsettings.setResolution(newResolution);
+
+ m_session->setVideoSettings(tsettings);
+}
new file mode 100644
@@ -0,0 +1,28 @@
+#ifndef QOHVIDEOENCODERCONTROL_H
+#define QOHVIDEOENCODERCONTROL_H
+
+#include <qvideoencodersettingscontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCaptureSession;
+
+class QOhVideoEncoderControl : public QVideoEncoderSettingsControl
+{
+ Q_OBJECT
+public:
+ QOhVideoEncoderControl(QOhCaptureSession *session);
+
+ QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous = 0) const override;
+ QList<qreal> supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous = 0) const override;
+ QStringList supportedVideoCodecs() const override;
+ QString videoCodecDescription(const QString &codecName) const override;
+ QVideoEncoderSettings videoSettings() const override;
+ void setVideoSettings(const QVideoEncoderSettings &settings) override;
+private:
+ QOhCaptureSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHVIDEOENCODERCONTROL_H
new file mode 100644
@@ -0,0 +1,36 @@
+#include "qohcamerasession.h"
+#include "ohcamera/camera.h"
+#include "qohviewfindersettingscontrol2.h"
+
+QOhViewfinderSettingsControl2::QOhViewfinderSettingsControl2(QOhCameraSession *session)
+ : m_cameraSession(session)
+{
+}
+
+QList<QCameraViewfinderSettings> QOhViewfinderSettingsControl2::supportedViewfinderSettings() const
+{
+ Camera_OutputCapability *ability = m_cameraSession->cameraAbility();
+ if(!ability)
+ {
+ return {};
+ }
+
+ QList<QCameraViewfinderSettings> viewfinderSettings;
+
+ for(int i = 0; i < ability->previewProfilesSize; ++i)
+ {
+ viewfinderSettings << m_cameraSession->profileToSetting(ability->previewProfiles[i]);
+ }
+
+ return viewfinderSettings;
+}
+
+QCameraViewfinderSettings QOhViewfinderSettingsControl2::viewfinderSettings() const
+{
+ return m_cameraSession->viewfinderSettings();
+}
+
+void QOhViewfinderSettingsControl2::setViewfinderSettings(const QCameraViewfinderSettings &settings)
+{
+ m_cameraSession->setViewfinderSettings(settings);
+}
new file mode 100644
@@ -0,0 +1,25 @@
+#ifndef QOHVIEWFINDERSETTINGSCONTROL2_H
+#define QOHVIEWFINDERSETTINGSCONTROL2_H
+
+#include <QtMultimedia/qcameraviewfindersettingscontrol.h>
+#include <QtMultimedia/qcameraviewfindersettings.h>
+
+QT_BEGIN_NAMESPACE
+class QOhCameraSession;
+
+class QOhViewfinderSettingsControl2 : public QCameraViewfinderSettingsControl2
+{
+ Q_OBJECT
+public:
+ explicit QOhViewfinderSettingsControl2(QOhCameraSession *session);
+
+ QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
+ QCameraViewfinderSettings viewfinderSettings() const override;
+ void setViewfinderSettings(const QCameraViewfinderSettings &settings) override;
+private:
+ QOhCameraSession *m_cameraSession;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHVIEWFINDERSETTINGSCONTROL2_H
new file mode 100644
@@ -0,0 +1,4 @@
+{
+ "Keys": ["openharmonymultimedia"],
+ "Services": ["org.qt-project.qt.camera", "org.qt-project.qt.mediaplayer"]
+}
new file mode 100644
@@ -0,0 +1,33 @@
+INCLUDEPATH += $$PWD
+
+qtHaveModule(widgets): QT += widgets
+
+HEADERS += \
+ $$PWD/private/qohcodekit_p.h \
+ $$PWD/qohcodekit.h \
+ $$PWD/qohdecoder.h \
+ $$PWD/qohdemuxer.h \
+ $$PWD/qohplayer.h \
+ $$PWD/qohplayercontrol.h \
+ $$PWD/qohplayerservice.h \
+ $$PWD/qohvideorenderercontrol.h \
+ $$PWD/qohnativevideobuffer.h \
+ $$PWD/qohaudiorolecontrol.h \
+ $$PWD/qohcustomaudiorolecontrol.h \
+ $$PWD/qohwindowcontrol.h
+
+SOURCES += \
+ $$PWD/qohcodekit.cpp \
+ $$PWD/qohdecoder.cpp \
+ $$PWD/qohdemuxer.cpp \
+ $$PWD/qohplayer.cpp \
+ $$PWD/qohplayercontrol.cpp \
+ $$PWD/qohplayerservice.cpp \
+ $$PWD/qohvideorenderercontrol.cpp \
+ $$PWD/qohnativevideobuffer.cpp \
+ $$PWD/qohaudiorolecontrol.cpp \
+ $$PWD/qohcustomaudiorolecontrol.cpp \
+ $$PWD/qohwindowcontrol.cpp
+
+LIBS += -lnative_image -lnative_buffer -lnative_window
+
new file mode 100644
@@ -0,0 +1,23 @@
+#ifndef QOHCODEKIT_P_H
+#define QOHCODEKIT_P_H
+
+#include <qglobal.h>
+
+QT_BEGIN_NAMESPACE
+class QOhCodeKit;
+class QOhCodeKitPrivate
+{
+ Q_DECLARE_PUBLIC(QOhCodeKit)
+ Q_DISABLE_COPY_MOVE(QOhCodeKitPrivate)
+
+public:
+ explicit QOhCodeKitPrivate(QOhCodeKit *const qq);
+ virtual ~QOhCodeKitPrivate();
+
+protected:
+ QOhCodeKit *const q_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHCODEKIT_P_H
new file mode 100644
@@ -0,0 +1,39 @@
+#include "qohaudiorolecontrol.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhAudioRoleControl::QOhAudioRoleControl(QObject *parent)
+ : QAudioRoleControl(parent)
+{
+}
+
+QAudio::Role QOhAudioRoleControl::audioRole() const
+{
+ return m_role;
+}
+
+void QOhAudioRoleControl::setAudioRole(QAudio::Role role)
+{
+ if (m_role == role)
+ return;
+
+ m_role = role;
+ emit audioRoleChanged(m_role);
+}
+
+QList<QAudio::Role> QOhAudioRoleControl::supportedAudioRoles() const
+{
+ return QList<QAudio::Role>()
+ << QAudio::UnknownRole
+ << QAudio::MusicRole
+ << QAudio::VideoRole
+ << QAudio::VoiceCommunicationRole
+ << QAudio::AlarmRole
+ << QAudio::NotificationRole
+ << QAudio::RingtoneRole
+ << QAudio::AccessibilityRole
+ << QAudio::GameRole
+ << QAudio::CustomRole;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,24 @@
+#ifndef QOHAUDIOROLECONTROL_H
+#define QOHAUDIOROLECONTROL_H
+
+#include <qaudiorolecontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhAudioRoleControl : public QAudioRoleControl
+{
+ Q_OBJECT
+public:
+ explicit QOhAudioRoleControl(QObject *parent = nullptr);
+
+ QAudio::Role audioRole() const override;
+ void setAudioRole(QAudio::Role role) override;
+ QList<QAudio::Role> supportedAudioRoles() const override;
+
+private:
+ QAudio::Role m_role = QAudio::UnknownRole;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHAUDIOROLECONTROL_H
new file mode 100644
@@ -0,0 +1,38 @@
+#include <QLoggingCategory>
+
+#include "qohcodekit.h"
+#include "private/qohcodekit_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(AvCoder, "qt.oh.avcoder")
+
+QOhCodeKitPrivate::QOhCodeKitPrivate(QOhCodeKit * const qq)
+ : q_ptr(qq)
+{
+
+}
+
+QOhCodeKitPrivate::~QOhCodeKitPrivate()
+{
+
+}
+
+QOhCodeKit::QOhCodeKit(QObject *parent) : QObject { parent }
+ , d_ptr(new QOhCodeKitPrivate(this))
+{
+}
+
+QOhCodeKit::~QOhCodeKit()
+{
+
+}
+
+QOhCodeKit::QOhCodeKit(QOhCodeKitPrivate &dd, QObject *parent)
+ : QObject { parent }
+ , d_ptr(&dd)
+{
+
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,46 @@
+#ifndef QOHCODEKIT_H
+#define QOHCODEKIT_H
+
+#include <QObject>
+#include <QScopedPointer>
+
+QT_BEGIN_NAMESPACE
+class QOhCodeKitPrivate;
+class QOhCodeKit : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QOhCodeKit)
+ Q_DISABLE_COPY_MOVE(QOhCodeKit)
+
+public:
+ struct Video {
+ int32_t width; /* 视频宽度 */
+ int32_t height; /* 视频高度 */
+ int64_t bitRate; /* 比特率 */
+ double frameRate; /* 视频帧率 */
+ int32_t rotation; /* 旋转角度 */
+ int32_t videoTrack; /* 视频轨道索引 */
+ int32_t pixelForamt; /* 视频像素格式 */
+ std::string codeMime; /* 编解码器MIME类型 */
+ };
+
+ struct Audio {
+ int32_t sampleRate; /* 音频采样率 */
+ int32_t audioTrack; /* 音频轨道索引 */
+ int32_t audioFormat; /* 音频原始格式 */
+ int32_t channelCount; /* 音频通道计数 */
+ std::string codeMime; /* 编解码器MIME类型 */
+ int64_t channelLayout; /* 所需编码通道布局-仅适用于编码器 */
+ };
+
+ explicit QOhCodeKit(QObject *parent = nullptr);
+ virtual ~QOhCodeKit();
+
+protected:
+ explicit QOhCodeKit(QOhCodeKitPrivate &dd, QObject *parent = nullptr);
+ QScopedPointer<QOhCodeKitPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHCODEKIT_H
new file mode 100644
@@ -0,0 +1,34 @@
+#include "qohcustomaudiorolecontrol.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhCustomAudioRoleControl::QOhCustomAudioRoleControl(QObject *parent)
+ : QCustomAudioRoleControl(parent)
+{
+}
+
+QString QOhCustomAudioRoleControl::customAudioRole() const
+{
+ return m_role;
+}
+
+void QOhCustomAudioRoleControl::setCustomAudioRole(const QString &role)
+{
+ if (m_role == role)
+ return;
+
+ m_role = role;
+ emit customAudioRoleChanged(m_role);
+}
+
+QStringList QOhCustomAudioRoleControl::supportedCustomAudioRoles() const
+{
+ return QStringList()
+ << "AUDIOSTREAM_USAGE_VOICE_ASSISTANT"
+ << "AUDIOSTREAM_USAGE_VOICE_MESSAGE"
+ << "AUDIOSTREAM_USAGE_AUDIOBOOK"
+ << "AUDIOSTREAM_USAGE_NAVIGATION"
+ << "AUDIOSTREAM_USAGE_VIDEO_COMMUNICATION";
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,24 @@
+#ifndef QOHCUSTOMAUDIOROLECONTROL_H
+#define QOHCUSTOMAUDIOROLECONTROL_H
+
+#include <qcustomaudiorolecontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhCustomAudioRoleControl : public QCustomAudioRoleControl
+{
+ Q_OBJECT
+public:
+ explicit QOhCustomAudioRoleControl(QObject *parent = nullptr);
+
+ QString customAudioRole() const override;
+ void setCustomAudioRole(const QString &role) override;
+ QStringList supportedCustomAudioRoles() const override;
+
+private:
+ QString m_role;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHCUSTOMAUDIOROLECONTROL_H
new file mode 100644
@@ -0,0 +1,407 @@
+/*******************************************************************
+ * Copyright(c) 2022-2025 ISS
+ * All right reserved. See LGPL for detailed Information
+ *
+ * 文件名称: qohdecoder.cpp
+ * 简要描述: 封装鸿蒙提供的解码器接口
+ * 创建日期: 2024/12/13
+ * 作者: WangHao
+ * 说明:
+ *
+ * 修改日期:
+ * 作者:
+ * 说明:
+ ******************************************************************/
+#include <QImage>
+#include <QDebug>
+#include <QVideoFrame>
+#include <QLoggingCategory>
+#include <QVideoSurfaceFormat>
+#include <qopenharmonydefines.h>
+#include <native_buffer/native_buffer.h>
+#include <multimedia/player_framework/native_avformat.h>
+#include <multimedia/player_framework/native_avbuffer.h>
+#include <multimedia/player_framework/native_avcodec_base.h>
+#include <multimedia/player_framework/native_avcodec_videodecoder.h>
+
+#include "qohdemuxer.h"
+#include "qohdecoder.h"
+#include "private/qohcodekit_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(AvCoder)
+
+class QOhVideoDecoderPrivate : public QOhCodeKitPrivate
+{
+ Q_DECLARE_PUBLIC(QOhVideoDecoder)
+ Q_DISABLE_COPY_MOVE(QOhVideoDecoderPrivate)
+
+public:
+ struct Frame {
+ int32_t width; /* 视频宽度 */
+ int32_t height; /* 视频高度 */
+ int32_t stride; /* 视频帧宽跨距 */
+ int32_t cropTop; /* 裁剪矩形顶部坐标(y)值 */
+ int32_t cropLeft; /* 裁剪矩形左坐标(x)值 */
+ int32_t cropRight; /* 裁剪矩形右坐标(x)值 */
+ int32_t cropBottom; /* 裁剪矩形底部坐标(y)值 */
+ int32_t sliceStride; /* 视频帧高跨距 */
+ int32_t pixelFormat; /* 视频像素格式 */
+ };
+ explicit QOhVideoDecoderPrivate(QOhDemuxer *const demuxer,
+ QOhVideoDecoder *const qq);
+ ~QOhVideoDecoderPrivate();
+
+protected:
+ void updateFrame(OH_AVFormat *format);
+ QVideoFrame fromRawData(uint8_t * const data, int32_t size, Frame frame);
+
+private:
+ static void OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData);
+ static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData);
+ static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData);
+ static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData);
+
+private:
+ QOhDemuxer *const m_demuxer;
+ QSharedPointer<Frame> m_frame;
+ QSharedPointer<OH_AVCodec> m_decoder;
+};
+
+QOhVideoDecoderPrivate::QOhVideoDecoderPrivate(QOhDemuxer * const demuxer, QOhVideoDecoder * const qq)
+ : QOhCodeKitPrivate(qq)
+ , m_demuxer(demuxer)
+ , m_frame(nullptr)
+ , m_decoder(nullptr)
+{
+
+}
+
+QOhVideoDecoderPrivate::~QOhVideoDecoderPrivate()
+{
+
+}
+
+void QOhVideoDecoderPrivate::updateFrame(OH_AVFormat *format)
+{
+ if (m_frame.isNull())
+ m_frame.reset(new Frame());
+
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_STRIDE, &m_frame->stride);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_WIDTH, &m_frame->width);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_HEIGHT, &m_frame->height);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, &m_frame->pixelFormat);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_SLICE_HEIGHT, &m_frame->sliceStride);
+ /* 获取裁剪矩形信息 */
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_CROP_TOP, &m_frame->cropTop);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_CROP_LEFT, &m_frame->cropLeft);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_CROP_RIGHT, &m_frame->cropRight);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_CROP_BOTTOM, &m_frame->cropBottom);
+}
+
+QVideoFrame QOhVideoDecoderPrivate::fromRawData(uint8_t * const data, int32_t size, Frame frame)
+{
+ switch (frame.pixelFormat) {
+ case AV_PIXEL_FORMAT_RGBA:
+ {
+ QVideoFrame videoFrame(size, QSize(frame.width, frame.height), frame.stride, QVideoFrame::Format_RGB32);
+ if (videoFrame.map(QAbstractVideoBuffer::WriteOnly)) {
+ memcpy(videoFrame.bits(), data, videoFrame.mappedBytes());
+ }
+ videoFrame.unmap();
+ return videoFrame;
+ }
+ case AV_PIXEL_FORMAT_YUVI420:
+ {
+ QVideoFrame videoFrame(size, QSize(frame.width, frame.height), frame.stride, QVideoFrame::Format_YUV420P);
+ if (videoFrame.map(QAbstractVideoBuffer::WriteOnly)) {
+ memcpy(videoFrame.bits(), data, videoFrame.mappedBytes());
+ }
+ videoFrame.unmap();
+ return videoFrame;
+ }
+ case AV_PIXEL_FORMAT_NV21:
+ {
+ QVideoFrame videoFrame(size, QSize(frame.width, frame.height), frame.stride, QVideoFrame::Format_NV21);
+ if (videoFrame.map(QAbstractVideoBuffer::WriteOnly)) {
+ memcpy(videoFrame.bits(), data, videoFrame.mappedBytes());
+ }
+ videoFrame.unmap();
+ return videoFrame;
+ }
+ case AV_PIXEL_FORMAT_NV12:
+ {
+ QVideoFrame videoFrame(size, QSize(frame.width, frame.height), frame.stride, QVideoFrame::Format_NV12);
+ if (videoFrame.map(QAbstractVideoBuffer::WriteOnly)) {
+ memcpy(videoFrame.bits(), data, videoFrame.mappedBytes());
+ }
+ videoFrame.unmap();
+ return videoFrame;
+ }
+ default:
+ break;
+ }
+ return QVideoFrame();
+}
+/*!
+ * \brief 解码异常回调OH_AVCodecOnError实现
+ * \param codec
+ * \param errorCode
+ * \param userData
+ */
+void QOhVideoDecoderPrivate::OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData)
+{
+ Q_UNUSED(codec);
+ Q_UNUSED(userData);
+ LOGE("on codec error, error code: %{public}d", errorCode);
+}
+/*!
+ * \brief 解码数据流变化回调OH_AVCodecOnStreamChanged实现
+ * \param codec
+ * \param format
+ * \param userData
+ */
+void QOhVideoDecoderPrivate::OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
+{
+ Q_UNUSED(codec);
+ Q_UNUSED(format);
+ Q_UNUSED(userData);
+
+ QOhVideoDecoder *decoder = reinterpret_cast<QOhVideoDecoder*>(userData);
+ if (!decoder)
+ return;
+
+ decoder->d_func()->updateFrame(format);
+
+ LOGI("on video codec format change");
+}
+/*!
+ * \brief 解码输入回调OH_AVCodecOnNeedInputBuffer实现
+ * \param codec
+ * \param index
+ * \param buffer
+ * \param userData
+ */
+void QOhVideoDecoderPrivate::OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
+{
+ //LOGI("on video input buffer, codec: [%{public}p] index: [%{public}d]", codec, index);
+ qCInfo(AvCoder) << "on video input buffer, codec: [" << codec << "] index: [" << index << ']';
+
+ Q_UNUSED(codec);
+ if (nullptr == userData)
+ return;
+
+ QOhVideoDecoder *decoder = reinterpret_cast<QOhVideoDecoder*>(userData);
+ if (!decoder)
+ return;
+
+ QOhDemuxer *demuxer = decoder->d_func()->m_demuxer;
+ if (!demuxer)
+ return;
+
+ if (!demuxer->readSample(demuxer->videoInfo()->videoTrack, buffer)) {
+ LOGW("read video sample buffer failed.");
+ return;
+ }
+
+ OH_VideoDecoder_PushInputBuffer(codec, index);
+}
+/*!
+ * \brief 解码输出回调OH_AVCodecOnNewOutputBuffer实现
+ * \param codec
+ * \param index
+ * \param buffer
+ * \param userData
+ */
+void QOhVideoDecoderPrivate::OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
+{
+ //LOGI("on video output buffer, codec: [%{public}p] index: [%{public}d]", codec, index);
+ qCInfo(AvCoder) << "on video output buffer, codec: [" << codec << "] index: [" << index << ']';
+ if (nullptr == userData) {
+ return;
+ }
+
+ QOhVideoDecoder *decoder = reinterpret_cast<QOhVideoDecoder*>(userData);
+ if (!decoder)
+ return;
+
+ QOhDemuxer *demuxer = decoder->d_func()->m_demuxer;
+ if (!demuxer)
+ return;
+
+ static bool firstFrame = true;
+ if (firstFrame) {
+ OH_AVFormat *format = OH_VideoDecoder_GetOutputDescription(codec);
+ decoder->d_func()->updateFrame(format);
+
+ firstFrame = false;
+ OH_AVFormat_Destroy(format);
+ }
+
+ /* AVCODEC_BUFFER_FLAGS_NONE 默认
+ * AVCODEC_BUFFER_FLAGS_EOS 结尾sample,数据为空
+ * AVCODEC_BUFFER_FLAGS_SYNC_FRAME IDR帧或I帧
+ * AVCODEC_BUFFER_FLAGS_INCOMPLETE_FRAME 非完整的sample,一般由于buffer过小,无法拷贝完整的sample
+ * AVCODEC_BUFFER_FLAGS_CODEC_DATA 含参数集信息的帧
+ * AVCODEC_BUFFER_FLAGS_DISCARD 可丢弃的帧
+ */
+ OH_AVCodecBufferAttr attr = { 0, 0, 0, AVCODEC_BUFFER_FLAGS_NONE };
+ int ret = OH_AVBuffer_GetBufferAttr(buffer, &attr);
+ if (AV_ERR_OK == ret && (AVCODEC_BUFFER_FLAGS_EOS & attr.flags)) {
+ OH_VideoDecoder_FreeOutputBuffer(codec, index);
+ Q_EMIT decoder->finished(QOhVideoDecoder::QPrivateSignal());
+ return;
+ }
+
+ /* TODO 是否必要线程处理 */
+ uint8_t *data = OH_AVBuffer_GetAddr(buffer);
+ Frame *frameInfo = decoder->d_func()->m_frame.get();
+ const QVideoFrame &frame = decoder->d_func()->fromRawData(data, attr.size, *frameInfo);
+ Q_EMIT decoder->videoFrameChange(frame, QOhVideoDecoder::QPrivateSignal());
+
+ OH_VideoDecoder_FreeOutputBuffer(codec, index);
+}
+
+QOhVideoDecoder::QOhVideoDecoder(QOhDemuxer *const demuxer, QObject *parent)
+ : QOhCodeKit(*(new QOhVideoDecoderPrivate(demuxer, this)), parent)
+{
+}
+
+QOhVideoDecoder::~QOhVideoDecoder()
+{
+
+}
+
+bool QOhVideoDecoder::stop()
+{
+ Q_D(QOhVideoDecoder);
+ if (d->m_decoder.isNull()) {
+ qCCritical(AvCoder) << "video decoder is null, call configure function first.";
+ return false;
+ }
+
+ OH_VideoDecoder_Flush(d->m_decoder.get());
+ int ret = OH_VideoDecoder_Stop(d->m_decoder.get());
+ if (AV_ERR_OK != ret) {
+ qCCritical(AvCoder) << "stop video decoder failed. ret: " << ret;
+ return false;
+ }
+ return true;
+}
+
+bool QOhVideoDecoder::start()
+{
+ Q_D(QOhVideoDecoder);
+ if (d->m_decoder.isNull()) {
+ qCCritical(AvCoder) << "video decoder is null, call configure function first.";
+ return false;
+ }
+
+ OH_VideoDecoder_Flush(d->m_decoder.get());
+ int ret = OH_VideoDecoder_Start(d->m_decoder.get());
+ if (AV_ERR_OK != ret) {
+ qCCritical(AvCoder) << "start video decoder failed. ret: " << ret;
+ return false;
+ }
+ return true;
+}
+
+bool QOhVideoDecoder::reset()
+{
+ Q_D(QOhVideoDecoder);
+ if (d->m_decoder.isNull()) {
+ qCCritical(AvCoder) << "video decoder is null, call configure function first.";
+ return false;
+ }
+
+ OH_VideoDecoder_Flush(d->m_decoder.get());
+ int ret = OH_VideoDecoder_Reset(d->m_decoder.get());
+ if (AV_ERR_OK != ret) {
+ qCCritical(AvCoder) << "reset video decoder failed, ret: " << ret;
+ return false;
+ }
+ return true;
+}
+
+bool QOhVideoDecoder::prepare()
+{
+ Q_D(QOhVideoDecoder);
+ int ret = OH_VideoDecoder_Prepare(d->m_decoder.get());
+ if (AV_ERR_OK != ret) {
+ qCCritical(AvCoder) << "video decoder prepare failed. ret: " << ret;
+ return false;
+ }
+ return true;
+}
+
+bool QOhVideoDecoder::configure(QSharedPointer<Video> info)
+{
+ OH_AVCodec *code = OH_VideoDecoder_CreateByMime(info->codeMime.c_str());
+ if (nullptr == code) {
+ qCCritical(AvCoder) << "create video decoder failed.";
+ return false;
+ }
+
+ auto format = QSharedPointer<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
+ if (!format) {
+ qCCritical(AvCoder) << "create video decoder format failed.";
+ return false;
+ }
+
+ bool result = false;
+
+ result = OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, info->width);
+ qInfo() << tr("set video format width: %1 value: %2").arg(result).arg(info->width);
+
+ result = OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, info->height);
+ qInfo() << tr("set video format height: %1 value: %2").arg(result).arg(info->height);
+
+#if 0
+ /* 配置低时延解码 */
+ result = OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_VIDEO_ENABLE_LOW_LATENCY, 1);
+ qInfo() << tr("set video format LOW_LATENCY: %1").arg(result);
+#endif
+
+ result = OH_AVFormat_SetDoubleValue(format.get(), OH_MD_KEY_FRAME_RATE, info->frameRate);
+ qInfo() << tr("set video format frameRate: %1 value: %2").arg(result).arg(info->frameRate);
+
+#if 0
+ /* FIXME 不能持预设图片格式 */
+ result = OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, info->pixelForamt);
+ qInfo() << tr("set video format pixelForamt: %1 value: %2").arg(result).arg(info->pixelForamt);
+#endif
+
+#if 0
+ /* 只在视频解码Surface模式下使用 */
+ if (info->height > info->width)
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, info->rotation + 90);
+#endif
+
+ int ret = OH_VideoDecoder_Configure(code, format.get());
+ if (AV_ERR_OK != ret) {
+ qCCritical(AvCoder) << "configure video decoder failed, ret: " << ret;
+ return false;
+ }
+
+ OH_AVCodecCallback cb = { &QOhVideoDecoderPrivate::OnCodecError,
+ &QOhVideoDecoderPrivate::OnStreamChanged,
+ &QOhVideoDecoderPrivate::OnNeedInputBuffer,
+ &QOhVideoDecoderPrivate::OnNewOutputBuffer };
+
+ ret = OH_VideoDecoder_RegisterCallback(code, cb, reinterpret_cast<void*>(this));
+ if (AV_ERR_OK != ret) {
+ qCCritical(AvCoder) << "register video decoder callback failed. ret: " << ret;
+ return false;
+ }
+
+
+ Q_D(QOhVideoDecoder);
+ d->m_decoder = QSharedPointer<OH_AVCodec>(code, OH_VideoDecoder_Destroy);
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+
+
new file mode 100644
@@ -0,0 +1,54 @@
+/*******************************************************************
+ * Copyright(c) 2022-2025 ISS
+ * All right reserved. See LGPL for detailed Information
+ *
+ * 文件名称: qohdecoder.h
+ * 简要描述: 封装鸿蒙提供的解码器接口
+ * 创建日期: 2024/12/13
+ * 作者: WangHao
+ * 说明:
+ *
+ * 修改日期:
+ * 作者:
+ * 说明:
+ ******************************************************************/
+#ifndef QOHDECODER_H
+#define QOHDECODER_H
+
+#include <QObject>
+#include <QScopedPointer>
+
+#include "qohcodekit.h"
+
+QT_BEGIN_NAMESPACE
+class QOhDemuxer;
+class QVideoFrame;
+class QOhVideoDecoderPrivate;
+
+/*!
+ * \brief 视频解码
+ */
+class QOhVideoDecoder : public QOhCodeKit
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QOhVideoDecoder)
+ Q_DISABLE_COPY_MOVE(QOhVideoDecoder)
+
+public:
+ explicit QOhVideoDecoder(QOhDemuxer *const demuxer, QObject *parent = nullptr);
+ ~QOhVideoDecoder();
+
+ bool stop();
+ bool start();
+ bool reset();
+ bool prepare();
+ bool configure(QSharedPointer<QOhCodeKit::Video> info);
+
+Q_SIGNALS:
+ void finished(QPrivateSignal);
+ void videoFrameChange(const QVideoFrame&, QPrivateSignal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHDECODER_H
new file mode 100644
@@ -0,0 +1,336 @@
+/*******************************************************************
+ * Copyright(c) 2022-2025 ISS
+ * All right reserved. See LGPL for detailed Information
+ *
+ * 文件名称: qohdemuxer.cpp
+ * 简要描述: 封装鸿蒙接口,提供从媒体文件码流中提取sample的接口
+ * 创建日期: 2024/12/13
+ * 作者: WangHao
+ * 说明:
+ *
+ * 修改日期:
+ * 作者:
+ * 说明:
+ ******************************************************************/
+#include <QUrl>
+#include <QFile>
+#include <QDebug>
+#include <QSharedPointer>
+#include <QLoggingCategory>
+#include <qopenharmonydefines.h>
+#include <multimedia/player_framework/native_avsource.h>
+#include <multimedia/player_framework/native_avformat.h>
+#include <multimedia/player_framework/native_avbuffer.h>
+#include <multimedia/player_framework/native_avdemuxer.h>
+#include <multimedia/player_framework/native_avcodec_base.h>
+
+#include "qohdemuxer.h"
+#include "private/qohcodekit_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(AvCoder)
+class QOhDemuxerPrivate : public QOhCodeKitPrivate
+{
+ Q_DECLARE_PUBLIC(QOhDemuxer)
+ Q_DISABLE_COPY_MOVE(QOhDemuxerPrivate)
+
+public:
+ explicit QOhDemuxerPrivate(QOhDemuxer *const qq);
+ ~QOhDemuxerPrivate();
+
+ /* 创建媒体源 */
+ QSharedPointer<OH_AVSource> createSource(const QUrl &url);
+
+ /* 创建分流器 */
+ QSharedPointer<OH_AVDemuxer> createDemuxer(const QSharedPointer<OH_AVSource> source);
+
+ /* 媒体轨道选择 */
+ bool selectTrackType(const QSharedPointer<OH_AVSource> source,
+ const QSharedPointer<OH_AVDemuxer> demuxer);
+
+private:
+ QOhDemuxer::Type m_type;
+
+ /* 媒体源 */
+ QSharedPointer<OH_AVSource> m_source;
+ /* 解封装器 */
+ QSharedPointer<OH_AVDemuxer> m_demuxer;
+ /* 视频信息 */
+ QSharedPointer<QOhCodeKit::Video> m_videoInfo;
+ QSharedPointer<QOhCodeKit::Audio> m_audioInfo;
+};
+
+QOhDemuxerPrivate::QOhDemuxerPrivate(QOhDemuxer * const qq)
+ : QOhCodeKitPrivate(qq)
+ , m_type(QOhDemuxer::Type::E_NONE)
+ , m_source(nullptr)
+ , m_demuxer(nullptr)
+ , m_videoInfo(nullptr)
+ , m_audioInfo(nullptr)
+{
+
+}
+
+QOhDemuxerPrivate::~QOhDemuxerPrivate()
+{
+}
+
+QSharedPointer<OH_AVSource> QOhDemuxerPrivate::createSource(const QUrl &url)
+{
+ if (url.isEmpty())
+ return nullptr;
+
+ QSharedPointer<OH_AVSource> avSource(nullptr);
+ if (0 == QString::compare(url.scheme(), "file")) {
+ const QString &path = url.path();
+ QFile *file = new QFile(path);
+ if (!file->open(QIODevice::ReadOnly)) {
+ file->close();
+ qCCritical(AvCoder) << QObject::tr("open file: %1 failed, cause: %2.").arg(path, file->errorString());
+ file->deleteLater();
+ return nullptr;
+ }
+
+ OH_AVSource *source = OH_AVSource_CreateWithFD(file->handle(), 0, file->size());
+ if (nullptr == source) {
+ file->close();
+ qCCritical(AvCoder) << Q_FUNC_INFO << QObject::tr("call OH_AVSource_CreateWithFD failed.");
+ file->deleteLater();
+ return nullptr;
+ }
+
+ /* 为 fd 资源文件创建 source 资源对象
+ * 传入 offset 不为文件起始位置 或 size 不为文件大小时
+ * 可能会因不能获取完整数据导致 source 创建失败,或后续解封装失败等问题.
+ */
+ avSource = QSharedPointer<OH_AVSource>(source, [file](OH_AVSource *source){
+ OH_AVSource_Destroy(source);
+ file->close();
+ file->deleteLater();
+ });
+ } else {
+ /* 为 uri 资源文件创建 source 资源对象 */
+ const QString &path = url.path();
+ OH_AVSource *source = OH_AVSource_CreateWithURI(path.toLocal8Bit().data());
+ if (nullptr == source) {
+ qCCritical(AvCoder) << Q_FUNC_INFO << QObject::tr("call OH_AVSource_CreateWithURI failed.");
+ return nullptr;
+ }
+
+ avSource = QSharedPointer<OH_AVSource>(source, OH_AVSource_Destroy);
+ }
+
+ return avSource;
+}
+
+QSharedPointer<OH_AVDemuxer> QOhDemuxerPrivate::createDemuxer(const QSharedPointer<OH_AVSource> source)
+{
+ OH_AVDemuxer *demuxer = OH_AVDemuxer_CreateWithSource(source.get());
+ if (nullptr == demuxer) {
+ qCCritical(AvCoder) << "call OH_AVDemuxer_CreateWithSource failed.";
+ return nullptr;
+ }
+
+ return QSharedPointer<OH_AVDemuxer>(demuxer, OH_AVDemuxer_Destroy);
+}
+
+bool QOhDemuxerPrivate::selectTrackType(const QSharedPointer<OH_AVSource> source,
+ const QSharedPointer<OH_AVDemuxer> demuxer)
+{
+ OH_AVFormat *sfm = OH_AVSource_GetSourceFormat(source.get());
+ if (nullptr == sfm) {
+ qWarning() << "get source format failed.";
+ return false;
+ }
+
+ QSharedPointer<OH_AVFormat> format(sfm, OH_AVFormat_Destroy);
+ int32_t trackCount = 0;
+ if (!OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_TRACK_COUNT, &trackCount)) {
+ qWarning() << "get track count from source format failed.";
+ return false;
+ }
+
+
+ for (int32_t index = 0; index < trackCount; ++index) {
+ OH_AVFormat* tfm = OH_AVSource_GetTrackFormat(source.get(), index);
+ if (nullptr == tfm)
+ continue;
+
+ int trackType = -1;
+ auto trackFormat = QSharedPointer<OH_AVFormat>(tfm, OH_AVFormat_Destroy);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_TRACK_TYPE, &trackType);
+
+ /* 视频轨 */
+ if ((QOhDemuxer::E_VIDEO == (QOhDemuxer::E_VIDEO & m_type)) &&
+ MEDIA_TYPE_VID == trackType)
+ {
+ m_videoInfo.reset(new QOhCodeKit::Video());
+
+ char *codeMime;
+ bool result = false;
+ m_videoInfo->videoTrack = index;
+ OH_AVDemuxer_SelectTrackByID(demuxer.get(), index);
+
+ result = OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_WIDTH, &m_videoInfo->width);
+ qInfo() << QObject::tr("get video format width: %1 value: %2").arg(result).arg(m_videoInfo->width);
+
+ result = OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_HEIGHT, &m_videoInfo->height);
+ qInfo() << QObject::tr("get video format height: %1 value: %2").arg(result).arg(m_videoInfo->height);
+
+ result = OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_BITRATE, &m_videoInfo->bitRate);
+ qInfo() << QObject::tr("get video format bitRate: %1 value: %2").arg(result).arg(m_videoInfo->bitRate);
+
+ result = OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_ROTATION, &m_videoInfo->rotation);
+ qInfo() << QObject::tr("get video format rotation: %1 value: %2").arg(result).arg(m_videoInfo->rotation);
+
+ result = OH_AVFormat_GetDoubleValue(trackFormat.get(), OH_MD_KEY_FRAME_RATE, &m_videoInfo->frameRate);
+ qInfo() << QObject::tr("get video format frameRate: %1 value: %2").arg(result).arg(m_videoInfo->frameRate);
+
+ result = OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_PIXEL_FORMAT, &m_videoInfo->pixelForamt);
+ qInfo() << QObject::tr("get video format pixelForamt: %1 value: %2").arg(result).arg(m_videoInfo->pixelForamt);
+
+ result = OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, const_cast<char const **>(&codeMime));
+ qInfo() << QObject::tr("get video format codeMime: %1 value: %2").arg(result).arg(codeMime);
+
+ m_videoInfo->codeMime = std::string(codeMime);
+
+ LOGI("====== QOhDemuxer Video config ======");
+ LOGI("%{public}d*%{public}d, %{public}.1ffps, %{public}d kbps", m_videoInfo->width,
+ m_videoInfo->height, m_videoInfo->frameRate, m_videoInfo->bitRate / 1024);
+ LOGI("====== QOhDemuxer Video config ======");
+ }
+
+ /* 音频轨 */
+ if ((QOhDemuxer::E_AUDIO == (QOhDemuxer::E_AUDIO & m_type)) &&
+ MEDIA_TYPE_AUD == trackType)
+ {
+ m_audioInfo.reset(new QOhCodeKit::Audio());
+
+ char *codeMime;
+ bool result = false;
+ m_audioInfo->audioTrack = index;
+ OH_AVDemuxer_SelectTrackByID(demuxer.get(), index);
+
+ result = OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUD_SAMPLE_RATE, &m_audioInfo->sampleRate);
+ qInfo() << QObject::tr("get audio format sampleRate: %1 value: %2").arg(result).arg(m_audioInfo->sampleRate);
+
+ result = OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUDIO_SAMPLE_FORMAT, &m_audioInfo->audioFormat);
+ qInfo() << QObject::tr("get audio format audioFormat: %1 value: %2").arg(result).arg(m_audioInfo->audioFormat);
+
+ result = OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUD_CHANNEL_COUNT, &m_audioInfo->channelCount);
+ qInfo() << QObject::tr("get audio format channelCount: %1 value: %2").arg(result).arg(m_audioInfo->channelCount);
+
+ result = OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_CHANNEL_LAYOUT, &m_audioInfo->channelLayout);
+ qInfo() << QObject::tr("get audio format channelLayout: %1 value: %2").arg(result).arg(m_audioInfo->channelLayout);
+
+ result = OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, const_cast<char const **>(&codeMime));
+ qInfo() << QObject::tr("get audio format codeMime: %1 value: %2").arg(result).arg(codeMime);
+
+ m_audioInfo->codeMime = std::string(codeMime);
+
+ LOGI("====== QOhDemuxer Audio config ======");
+ LOGI("Mime: %{public}s", codeMime);
+ LOGI("audioMime:%{public}s sampleForamt:%{public}d "
+ "sampleRate:%{public}d channelCount:%{public}d channelLayout:%{public}d",
+ m_audioInfo->codeMime.c_str(),
+ m_audioInfo->audioFormat,
+ m_audioInfo->sampleRate,
+ m_audioInfo->channelCount,
+ (int)m_audioInfo->channelLayout);
+ LOGI("====== QOhDemuxer Audio config ======");
+ }
+ }
+
+ return true;
+}
+
+QOhDemuxer::QOhDemuxer(Type type)
+ : QOhCodeKit(*(new QOhDemuxerPrivate(this)))
+{
+ d_func()->m_type = type;
+}
+
+QOhDemuxer::~QOhDemuxer()
+{
+
+}
+
+bool QOhDemuxer::isValid() const
+{
+ Q_D(const QOhDemuxer);
+ return (!d->m_source.isNull() && !d->m_demuxer.isNull());
+}
+
+bool QOhDemuxer::configure(const QUrl &url)
+{
+ Q_D(QOhDemuxer);
+ /* FIXME 以下调用顺序不可修改 */
+
+ /* 创建媒体源 */
+ d->m_source = d->createSource(url);
+ if (!d->m_source) {
+ qCCritical(AvCoder) << "create multimedia source failed.";
+ return false;
+ }
+
+ /* 创建分流器 */
+ d->m_demuxer = d->createDemuxer(d->m_source);
+ if (!d->m_demuxer) {
+ qCCritical(AvCoder) << "create demuxer failed.";
+ return false;
+ }
+
+ /* 选取媒体轨道-获取格式信息 */
+ if (!d->selectTrackType(d->m_source, d->m_demuxer)) {
+ qCCritical(AvCoder) << "select track failed.";
+ return false;
+ }
+
+ return true;
+}
+
+bool QOhDemuxer::seekTime(qint64 millisecond)
+{
+ Q_D(QOhDemuxer);
+ if (d->m_demuxer.isNull())
+ return false;
+
+ int ret = OH_AVDemuxer_SeekToTime(d->m_demuxer.get(), millisecond,
+ SEEK_MODE_CLOSEST_SYNC);
+
+ if (AV_ERR_OK != ret) {
+ return false;
+ qCCritical(AvCoder) << "seekTime failed, ret: " << ret;
+ }
+ return true;
+}
+
+QSharedPointer<QOhCodeKit::Audio> QOhDemuxer::audioInfo() const
+{
+ Q_D(const QOhDemuxer);
+ return d->m_audioInfo;
+}
+
+QSharedPointer<QOhCodeKit::Video> QOhDemuxer::videoInfo() const
+{
+ Q_D(const QOhDemuxer);
+ return d->m_videoInfo;
+}
+
+bool QOhDemuxer::readSample(int32_t trackId, OH_AVBuffer *buffer)
+{
+ Q_D(QOhDemuxer);
+ if (d->m_demuxer.isNull() || !buffer)
+ return false;
+
+ int ret = OH_AVDemuxer_ReadSampleBuffer(d->m_demuxer.get(), trackId, buffer);
+ if (AV_ERR_OK != ret) {
+ LOGE("read demuxer sample failed, ret: %{public}d", ret);
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,51 @@
+/*******************************************************************
+ * Copyright(c) 2022-2025 ISS
+ * All right reserved. See LGPL for detailed Information
+ *
+ * 文件名称: qohdemuxer.h
+ * 简要描述: 封装鸿蒙接口,提供从媒体文件码流中提取sample的接口
+ * 创建日期: 2024/12/13
+ * 作者: WangHao
+ * 说明:
+ *
+ * 修改日期:
+ * 作者:
+ * 说明:
+ ******************************************************************/
+#ifndef QOHDEMUXER_H
+#define QOHDEMUXER_H
+
+#include "qohcodekit.h"
+
+struct OH_AVBuffer;
+QT_BEGIN_NAMESPACE
+class QUrl;
+class QOhDemuxerPrivate;
+
+class QOhDemuxer : public QOhCodeKit
+{
+ Q_DECLARE_PRIVATE(QOhDemuxer)
+ Q_DISABLE_COPY_MOVE(QOhDemuxer)
+
+public:
+ enum Type {
+ E_NONE = 0x00,
+ E_AUDIO = 0x01,
+ E_VIDEO = 0x02,
+ };
+
+ Q_DECLARE_FLAGS(Types, Type)
+ explicit QOhDemuxer(Type type);
+ ~QOhDemuxer();
+
+ bool isValid() const;
+ bool configure(const QUrl &url);
+ bool seekTime(qint64 millisecond);
+ QSharedPointer<QOhCodeKit::Audio> audioInfo() const;
+ QSharedPointer<QOhCodeKit::Video> videoInfo() const;
+ bool readSample(int32_t trackId, OH_AVBuffer *buffer);
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QOhDemuxer::Types)
+QT_END_NAMESPACE
+
+#endif // QOHDEMUXER_H
new file mode 100644
@@ -0,0 +1,68 @@
+#include <QDebug>
+#include <unistd.h>
+#include <native_buffer/buffer_common.h>
+
+#include "qohnativevideobuffer.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhNativeVideoBuffer::QOhNativeVideoBuffer(uchar *dataCopy,
+ int dataSize,
+ const OH_NativeBuffer_Config &config,
+ int strideBytes)
+ : QAbstractVideoBuffer(QAbstractVideoBuffer::NoHandle)
+ , m_dataCopy(dataCopy)
+ , m_dataSize(dataSize)
+ , m_strideBytes(strideBytes > 0 ? strideBytes : config.stride)
+ , m_config(config)
+ , m_mapMode(NotMapped)
+{
+
+}
+
+QOhNativeVideoBuffer::~QOhNativeVideoBuffer()
+{
+ /* 确保已经 unmap */
+ if (m_mapMode != NotMapped)
+ unmap();
+
+ /* 释放拷贝的数据 */
+ delete[] m_dataCopy;
+ m_dataCopy = nullptr;
+}
+
+QAbstractVideoBuffer::MapMode QOhNativeVideoBuffer::mapMode() const
+{
+ return m_mapMode;
+}
+
+uchar *QOhNativeVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
+{
+ if (m_mapMode != NotMapped || !m_dataCopy) {
+ qWarning() << "QOhNativeVideoBuffer::map: buffer already mapped or dataCopy is null";
+ return nullptr;
+ }
+
+ if (mode == NotMapped)
+ return nullptr;
+
+ m_mapMode = mode;
+
+ if (bytesPerLine)
+ *bytesPerLine = m_strideBytes;
+
+ if (numBytes)
+ *numBytes = m_dataSize;
+
+ return m_dataCopy;
+}
+
+void QOhNativeVideoBuffer::unmap()
+{
+ if (m_mapMode == NotMapped)
+ return;
+
+ m_mapMode = NotMapped;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,35 @@
+#ifndef QOHNATIVEVIDEOBUFFER_H
+#define QOHNATIVEVIDEOBUFFER_H
+
+#include <QAbstractVideoBuffer>
+#include <native_image/native_image.h>
+#include <native_buffer/native_buffer.h>
+#include <native_window/external_window.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhNativeVideoBuffer : public QAbstractVideoBuffer
+{
+ Q_DISABLE_COPY_MOVE(QOhNativeVideoBuffer)
+public:
+ QOhNativeVideoBuffer(uchar *dataCopy,
+ int dataSize,
+ const OH_NativeBuffer_Config &config,
+ int strideBytes = 0); /* strideBytes=0 时自动根据 config.stride 计算 */
+ ~QOhNativeVideoBuffer() override;
+
+ MapMode mapMode() const override;
+ uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) override;
+ void unmap() override;
+
+private:
+ uchar *m_dataCopy; /* 已拷贝的像素数据 */
+ int m_dataSize; /* 数据总字节数 */
+ int m_strideBytes; /* 行跳(字节/行)*/
+ OH_NativeBuffer_Config m_config;
+ MapMode m_mapMode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHNATIVEVIDEOBUFFER_H
new file mode 100644
@@ -0,0 +1,439 @@
+#include <QUrl>
+#include <QFile>
+#include <QHash>
+#include <QDebug>
+#include <QString>
+#include <QReadWriteLock>
+#include <QNetworkRequest>
+#include <rawfile/raw_file_manager.h>
+#include <multimedia/player_framework/avplayer_base.h>
+
+#include "qohplayer.h"
+
+typedef QHash<OH_AVPlayer *, QOhPlayer *> MediaPlayers;
+Q_GLOBAL_STATIC(MediaPlayers, mediaPlayers)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
+
+QT_BEGIN_NAMESPACE
+
+QOhPlayer::QOhPlayer()
+ : QObject()
+ , m_state(AV_IDLE)
+{
+ QWriteLocker locker(rwLock);
+ m_player = OH_AVPlayer_Create();
+ if (m_player != nullptr) {
+ OH_AVPlayer_SetOnInfoCallback(m_player, &QOhPlayer::onPlayerInfo, Q_NULLPTR);
+ OH_AVPlayer_SetOnErrorCallback(m_player, &QOhPlayer::onPlayerError, Q_NULLPTR);
+ mediaPlayers->insert(m_player, this);
+ }
+}
+
+QOhPlayer::~QOhPlayer()
+{
+ if (m_player != nullptr) {
+ QWriteLocker locker(rwLock);
+ mediaPlayers->remove(m_player);
+ OH_AVPlayer_Release(m_player);
+ }
+}
+
+void QOhPlayer::release()
+{
+ OH_AVPlayer_Release(m_player);
+}
+
+void QOhPlayer::reset()
+{
+ OH_AVPlayer_Reset(m_player);
+}
+
+int QOhPlayer::getCurrentPosition()
+{
+ if ((m_state == AV_PLAYING)
+ || (m_state == AV_PAUSED)
+ || (m_state == AV_COMPLETED)) {
+ int time = 0;
+ OH_AVPlayer_GetCurrentTime(m_player, &time);
+ return time;
+ }
+ return 0;
+}
+
+int QOhPlayer::getDuration()
+{
+ if (m_state < AV_PREPARED)
+ return 0;
+ int duration = 0;
+ OH_AVPlayer_GetDuration(m_player, &duration);
+ return duration;
+}
+
+bool QOhPlayer::isPlaying()
+{
+ return m_state == AV_PLAYING;
+}
+
+int QOhPlayer::volume()
+{
+ return m_volume * 100;
+}
+
+bool QOhPlayer::isMuted()
+{
+ return qFuzzyCompare(m_volume, 0.0);
+}
+
+qreal QOhPlayer::playbackRate()
+{
+ qreal rate(1.0);
+
+ AVPlaybackSpeed speed;
+ OH_AVPlayer_GetPlaybackSpeed(m_player, &speed);
+ switch (speed) {
+ case AV_SPEED_FORWARD_0_75_X:
+ rate = 0.75;
+ break;
+ case AV_SPEED_FORWARD_1_00_X:
+ rate = 1.0;
+ break;
+ case AV_SPEED_FORWARD_1_25_X:
+ rate = 1.25;
+ break;
+ case AV_SPEED_FORWARD_1_75_X:
+ rate = 1.75;
+ break;
+ case AV_SPEED_FORWARD_2_00_X:
+ rate = 2.0;
+ break;
+ }
+ return rate;
+}
+
+void QOhPlayer::play()
+{
+ if (m_state == AV_PLAYING)
+ return;
+
+ if ((m_state == AV_PREPARED)
+ || (m_state == AV_PAUSED)
+ || (m_state == AV_COMPLETED)) {
+ prepare();
+ OH_AVPlayer_Play(m_player);
+ } else if (m_state == AV_STOPPED) {
+ m_isPendingPlaying = true;
+ prepare();
+ }
+ else {
+ m_isPendingPlaying = true;
+ }
+}
+
+void QOhPlayer::pause()
+{
+ if (m_state == AV_PLAYING)
+ OH_AVPlayer_Pause(m_player);
+}
+
+void QOhPlayer::stop()
+{
+ if ((m_state == AV_PLAYING) ||
+ (m_state == AV_COMPLETED) ||
+ (m_state == AV_PREPARED) ||
+ (m_state == AV_PAUSED)) {
+ OH_AVPlayer_Stop(m_player);
+ OH_AVPlayer_Reset(m_player);
+ }
+}
+
+void QOhPlayer::seekTo(qint32 msec)
+{
+ if ((m_state == AV_PLAYING) ||
+ (m_state == AV_PAUSED) ||
+ (m_state == AV_COMPLETED)) {
+ OH_AVPlayer_Seek(m_player, msec, AV_SEEK_NEXT_SYNC);
+ }
+}
+
+void QOhPlayer::setMuted(bool mute)
+{
+ if (mute) {
+ m_volumeBeforeMute = m_volume;
+ setVolume(0);
+ } else {
+ setVolume(m_volumeBeforeMute);
+ }
+}
+
+void QOhPlayer::setDataSource(const QNetworkRequest &request)
+{
+ if (AV_IDLE != m_state)
+ reset();
+
+ QUrl url = request.url();
+ if (url.isEmpty())
+ return;
+
+ QString scheme = url.scheme();
+ if (scheme == "file") {
+ QFile file(url.path());
+ if (file.open(QFile::ReadOnly)) {
+ int length = file.size();
+ int fd = file.handle();
+ OH_AVErrCode code = OH_AVPlayer_SetFDSource(m_player, fd, 0, length);
+ if (AV_ERR_OK != code) {
+ setErrorString(code, tr("player with a null pointer or url with an empty value"));
+ }
+ file.close();
+ } else {
+ qWarning() << "Could not open media:" << file.errorString();
+ emit mediaStatusChanged(QMediaPlayer::InvalidMedia);
+ }
+ } else {
+ QString urlString = url.toString();
+ QByteArray dataArray = urlString.toLatin1();
+ OH_AVErrCode code = OH_AVPlayer_SetURLSource(m_player, dataArray);
+ if (AV_ERR_OK != code) {
+ setErrorString(code, tr("player with a null pointer or url with an empty value"));
+ }
+ }
+}
+
+void QOhPlayer::prepare()
+{
+ OH_AVPlayer_Prepare(m_player);
+}
+
+void QOhPlayer::setVolume(int volume)
+{
+ m_volume = volume;
+ OH_AVPlayer_SetVolume(m_player, m_volume / 100.0, m_volume / 100.0);
+}
+
+bool QOhPlayer::setPlaybackRate(qreal rate)
+{
+ AVPlaybackSpeed speed;
+ if (rate < 0.75)
+ speed = AV_SPEED_FORWARD_0_75_X;
+ else if (rate < 1.0)
+ speed = AV_SPEED_FORWARD_1_00_X;
+ else if (rate < 1.25)
+ speed = AV_SPEED_FORWARD_1_25_X;
+ else if (rate < 1.75)
+ speed = AV_SPEED_FORWARD_1_75_X;
+ else
+ speed = AV_SPEED_FORWARD_2_00_X;
+ return OH_AVPlayer_SetPlaybackSpeed(m_player, speed) == AV_ERR_OK;
+}
+
+#include <native_window/external_window.h>
+void QOhPlayer::setDisplay(OHNativeWindow *window)
+{
+ if (window == nullptr)
+ return;
+ m_window = window;
+
+ if (m_state == AV_INITIALIZED) {
+ OH_AVErrCode result = OH_AVPlayer_SetVideoSurface(m_player, m_window);
+ qWarning() << "Set Video Surface Result" << result;
+ m_surfaceSetted = true;
+ OH_AVPlayer_Prepare(m_player);
+ }
+}
+
+OHNativeWindow *QOhPlayer::display() const
+{
+ return m_window;
+}
+
+void QOhPlayer::setErrorString(int code, const QString &errorString)
+{
+ emit error(code, errorString);
+}
+
+void QOhPlayer::setPlayerInfo(AVPlayerOnInfoType type, OH_AVFormat *infoBody)
+{
+ switch(type) {
+ case AV_INFO_TYPE_STATE_CHANGE: {
+ int32_t state(AV_IDLE);
+ OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_STATE, &state);
+ setState(state);
+ }
+ break;
+ case AV_INFO_TYPE_RESOLUTION_CHANGE:
+ obtainVideoSize();
+ emit videoAvailableChanged(true);
+ break;
+ case AV_INFO_TYPE_DURATION_UPDATE: {
+ int64_t duration(0);
+ OH_AVFormat_GetLongValue(infoBody, OH_PLAYER_DURATION, &duration);
+ emit durationChanged(duration);
+ }
+ break;
+ case AV_INFO_TYPE_POSITION_UPDATE: {
+ int32_t position(0);
+ OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_CURRENT_POSITION, &position);
+ emit progressChanged(position);
+ }
+ break;
+ case AV_INFO_TYPE_BUFFERING_UPDATE: {
+ int32_t type(0);
+ int32_t value(0);
+ OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_BUFFERING_TYPE, &type);
+ if (type == AVPLAYER_BUFFERING_START) {
+ emit bufferingChanged(0);
+ } else if (type == AVPLAYER_BUFFERING_END) {
+ emit bufferingChanged(100);
+ } else if (type == AVPLAYER_BUFFERING_PERCENT) {
+ OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_BUFFERING_VALUE, &value);
+ emit bufferingChanged(value);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void QOhPlayer::setState(int32_t state)
+{
+ m_state = AVPlayerState(state);
+ switch(state) {
+ case AV_IDLE:
+ //release(); /* NOTE 重置资源时不需要释放,否则引起崩溃 */
+ break;
+ case AV_INITIALIZED: // avplayer 设置播放源后触发该状态上报
+ if (!m_surfaceSetted && m_window != nullptr) {
+ OH_AVErrCode result = OH_AVPlayer_SetVideoSurface(m_player, m_window);
+ qWarning() << "Set Video Surface Result" << result;
+ m_surfaceSetted = true;
+ }
+ prepare();
+ break;
+ case AV_PREPARED: // prepare调用成功后上报该状态机
+ obtainVideoSize();
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+ if (m_isPendingPlaying) {
+ OH_AVPlayer_Play(m_player);
+ }
+ emit audioAvailableChanged(true);
+ emit seekableChanged(true);
+ break;
+ case AV_STOPPED:
+ emit seekableChanged(false);
+ break;
+ case AV_COMPLETED:
+ emit mediaStatusChanged(QMediaPlayer::EndOfMedia);
+ break;
+ case AV_RELEASED:
+ emit durationChanged(0);
+ emit progressChanged(0);
+
+ emit audioAvailableChanged(false);
+ emit videoAvailableChanged(false);
+ emit seekableChanged(true);
+ break;
+ default:
+ break;
+ }
+ stateChanged(state);
+}
+
+void QOhPlayer::setAudioRole(QAudio::Role role)
+{
+ OH_AudioStream_Usage ohAudioRole = AUDIOSTREAM_USAGE_UNKNOWN;
+
+ switch(role){
+ case QAudio::UnknownRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_UNKNOWN;
+ break;
+ case QAudio::MusicRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_MUSIC;
+ break;
+ case QAudio::VideoRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_MOVIE;
+ break;
+ case QAudio::VoiceCommunicationRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_VOICE_COMMUNICATION;
+ break;
+ case QAudio::AlarmRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_ALARM;
+ break;
+ case QAudio::NotificationRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_NOTIFICATION;
+ break;
+ case QAudio::RingtoneRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_RINGTONE;
+ break;
+ case QAudio::AccessibilityRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_ACCESSIBILITY;
+ break;
+ case QAudio::GameRole:
+ ohAudioRole = AUDIOSTREAM_USAGE_GAME;
+ break;
+ default:
+ break;
+ }
+
+ OH_AVPlayer_SetAudioRendererInfo(m_player, ohAudioRole);
+}
+
+void QOhPlayer::setCustomAudioRole(const QString &role)
+{
+ OH_AudioStream_Usage ohAudioRole = AUDIOSTREAM_USAGE_UNKNOWN;
+
+ if(role == QLatin1String("AUDIOSTREAM_USAGE_VOICE_ASSISTANT"))
+ ohAudioRole = AUDIOSTREAM_USAGE_VOICE_ASSISTANT;
+ else if(role == QLatin1String("AUDIOSTREAM_USAGE_VOICE_MESSAGE"))
+ ohAudioRole = AUDIOSTREAM_USAGE_VOICE_MESSAGE;
+ else if(role == QLatin1String("AUDIOSTREAM_USAGE_AUDIOBOOK"))
+ ohAudioRole = AUDIOSTREAM_USAGE_AUDIOBOOK;
+ else if(role == QLatin1String("AUDIOSTREAM_USAGE_NAVIGATION"))
+ ohAudioRole = AUDIOSTREAM_USAGE_NAVIGATION;
+ else if(role == QLatin1String("AUDIOSTREAM_USAGE_VIDEO_COMMUNICATION"))
+ ohAudioRole = AUDIOSTREAM_USAGE_VIDEO_COMMUNICATION;
+ else{
+ ohAudioRole = AUDIOSTREAM_USAGE_UNKNOWN;
+ }
+
+ OH_AVPlayer_SetAudioRendererInfo(m_player, ohAudioRole);
+}
+
+void QOhPlayer::obtainVideoSize()
+{
+ int32_t w, h = 0;
+ OH_AVErrCode result = OH_AVPlayer_GetVideoWidth(m_player, &w);
+ if (result != AV_ERR_OK)
+ return;
+ result = OH_AVPlayer_GetVideoHeight(m_player, &h);
+ if (result != AV_ERR_OK)
+ return;
+ emit videoSizeChanged(w, h);
+}
+
+void QOhPlayer::onPlayerInfo(OH_AVPlayer *player, AVPlayerOnInfoType type,
+ OH_AVFormat *infoBody, void *userData)
+{
+ Q_UNUSED(userData);
+ QOhPlayer *p = mediaPlayers->value(player);
+ if (p == nullptr)
+ return;
+ p->setPlayerInfo(type, infoBody);
+}
+
+void QOhPlayer::onPlayerError(OH_AVPlayer *player, int32_t errorCode,
+ const char *errorMsg, void *userData)
+{
+ Q_UNUSED(userData);
+ QOhPlayer *p = mediaPlayers->value(player);
+ if (p == nullptr)
+ return;
+ p->setErrorString(errorCode, QString::fromLatin1(errorMsg));
+}
+
+AVPlayerState QOhPlayer::state() const
+{
+ return m_state;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,82 @@
+#ifndef QOHPLAYER_H
+#define QOHPLAYER_H
+
+#include <cstdint>
+#include <QObject>
+#include <napi/native_api.h>
+#include <QtMultimedia/qmediaplayer.h>
+#include <multimedia/player_framework/avplayer.h>
+#include <multimedia/player_framework/native_avformat.h>
+#include <ohaudio/native_audiostream_base.h>
+
+class QNetworkRequest;
+struct NativeResourceManager;
+
+class QOhPlayer : public QObject
+{
+ Q_OBJECT
+public:
+ QOhPlayer();
+ ~QOhPlayer();
+
+ void release();
+ void reset();
+
+ int getCurrentPosition();
+ int getDuration();
+ bool isPlaying();
+ int volume();
+ bool isMuted();
+ qreal playbackRate();
+
+ void play();
+ void pause();
+ void stop();
+ void seekTo(qint32 msec);
+ void setMuted(bool mute);
+ void setDataSource(const QNetworkRequest &request);
+ void prepare();
+ void setVolume(int volume);
+ bool setPlaybackRate(qreal rate);
+ void setDisplay(OHNativeWindow *window);
+ OHNativeWindow *display() const;
+
+
+ void setErrorString(int code, const QString &errorString);
+ void setPlayerInfo(AVPlayerOnInfoType type, OH_AVFormat *infoBody);
+
+ AVPlayerState state() const;
+ void setState(int32_t state);
+
+ void setAudioRole(QAudio::Role role);
+ void setCustomAudioRole(const QString &role);
+
+Q_SIGNALS:
+ void error(int code, const QString &errorString);
+ void bufferingChanged(qint32 percent);
+ void durationChanged(qint64 duration);
+ void progressChanged(qint64 progress);
+ void stateChanged(qint32 state);
+ void videoSizeChanged(qint32 width, qint32 height);
+ void audioAvailableChanged(bool available);
+ void videoAvailableChanged(bool available);
+ void seekableChanged(bool seekable);
+ void mediaStatusChanged(QMediaPlayer::MediaStatus status);
+private:
+ void obtainVideoSize();
+ void setPlayerData();
+private:
+ static void onPlayerInfo(OH_AVPlayer *player, AVPlayerOnInfoType type, OH_AVFormat *infoBody, void *userData);
+ static void onPlayerError(OH_AVPlayer *player, int32_t errorCode, const char *errorMsg, void *userData);
+private:
+ OH_AVPlayer *m_player = nullptr;
+ int m_volume = 0;
+ int m_volumeBeforeMute = 0;
+ OHNativeWindow *m_window = nullptr;
+ bool m_surfaceSetted = false;
+ AVPlayerState m_state;
+ bool m_isPendingPlaying = false;
+ QUrl m_url;
+};
+
+#endif // QOHPLAYER_H
new file mode 100644
@@ -0,0 +1,381 @@
+#include <qaudio.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qcoreapplication.h>
+#include <multimedia/player_framework/avplayer.h>
+
+#include "qohplayer.h"
+#include "qohvideorenderercontrol.h"
+#include "qohplayercontrol.h"
+#include "qohplayerservice.h"
+#include "qohwindowcontrol.h"
+
+QOhPlayerControl::QOhPlayerControl(QObject *parent)
+ : QMediaPlayerControl(parent)
+{
+ m_player = new QOhPlayer();
+ connect(m_player,SIGNAL(bufferingChanged(qint32)),
+ this,SLOT(onBufferingChanged(qint32)));
+ connect(m_player,SIGNAL(error(int,QString)),
+ this,SLOT(onError(int,QString)));
+ connect(m_player,SIGNAL(stateChanged(qint32)),
+ this,SLOT(onStateChanged(qint32)));
+ connect(m_player,SIGNAL(videoSizeChanged(qint32,qint32)),
+ this,SLOT(onVideoSizeChanged(qint32,qint32)));
+ connect(m_player,SIGNAL(progressChanged(qint64)),
+ this,SIGNAL(positionChanged(qint64)));
+ connect(m_player,SIGNAL(durationChanged(qint64)),
+ this,SIGNAL(durationChanged(qint64)));
+ connect(m_player,SIGNAL(audioAvailableChanged(bool)),
+ this,SLOT(setAudioAvailable(bool)));
+ connect(m_player,SIGNAL(videoAvailableChanged(bool)),
+ this,SLOT(setVideoAvailable(bool)));
+ connect(m_player,SIGNAL(seekableChanged(bool)),
+ this,SLOT(setSeekable(bool)));
+ connect(m_player,SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
+ this,SLOT(setMediaStatus(QMediaPlayer::MediaStatus)));
+}
+
+QOhPlayerControl::~QOhPlayerControl()
+{
+ m_player->release();
+ delete m_player;
+}
+
+void QOhPlayerControl::setVideoRenderOutput(QOhVideoRendererControl *control)
+{
+ m_videoRenderControl = control;
+}
+
+void QOhPlayerControl::setVideoWindowOutput(QOhWindowControl *control)
+{
+ m_videoWindowcControl = control;
+
+ if (nullptr != m_videoWindowcControl)
+ connect(m_videoWindowcControl, &QOhWindowControl::windowCreated, this, &QOhPlayerControl::onWindowCreated);
+}
+
+QMediaPlayer::State QOhPlayerControl::state() const
+{
+ AVPlayerState state = m_player->state();
+ if (state == AV_PLAYING)
+ return QMediaPlayer::PlayingState;
+ else if (state == AV_PAUSED)
+ return QMediaPlayer::PausedState;
+ else
+ return QMediaPlayer::StoppedState;
+}
+
+QMediaPlayer::MediaStatus QOhPlayerControl::mediaStatus() const
+{
+ return m_status;
+}
+
+qint64 QOhPlayerControl::duration() const
+{
+ return m_player->getDuration();
+}
+
+qint64 QOhPlayerControl::position() const
+{
+ if (m_status == QMediaPlayer::EndOfMedia)
+ return duration();
+ return m_player->getCurrentPosition();
+}
+
+void QOhPlayerControl::setPosition(qint64 position)
+{
+ if (m_status == QMediaPlayer::EndOfMedia) {
+ m_status = QMediaPlayer::LoadedMedia;
+ emit mediaStatusChanged(m_status);
+ }
+ const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
+
+ if (seekPosition == this->position())
+ return;
+
+ m_player->seekTo(seekPosition);
+ /* NativeImage 方案下 seek 由 AVPlayer 统一处理,无需单独通知 videoRenderControl */
+
+ Q_EMIT positionChanged(seekPosition);
+}
+
+int QOhPlayerControl::volume() const
+{
+ return m_volume;
+}
+
+void QOhPlayerControl::setVolume(int volume)
+{
+ if (m_volume == volume)
+ return;
+ m_player->setVolume(volume);
+
+ Q_EMIT volumeChanged(volume);
+}
+
+bool QOhPlayerControl::isMuted() const
+{
+ return m_muted;
+}
+
+void QOhPlayerControl::setMuted(bool muted)
+{
+ if (m_muted == muted)
+ return;
+
+ m_player->setMuted(muted);
+
+ Q_EMIT mutedChanged(muted);
+}
+
+int QOhPlayerControl::bufferStatus() const
+{
+ return m_bufferFilled ? 100 : 0;
+}
+
+bool QOhPlayerControl::isAudioAvailable() const
+{
+ return m_audioAvailable;
+}
+
+bool QOhPlayerControl::isVideoAvailable() const
+{
+ return m_videoAvailable;
+}
+
+bool QOhPlayerControl::isSeekable() const
+{
+ return m_seekable;
+}
+
+QMediaTimeRange QOhPlayerControl::availablePlaybackRanges() const
+{
+ return m_availablePlaybackRange;
+}
+
+void QOhPlayerControl::updateAvailablePlaybackRanges()
+{
+ if (m_buffering) {
+ const qint64 pos = position();
+ const qint64 end = (duration() / 100) * m_bufferPercent;
+ m_availablePlaybackRange.addInterval(pos, end);
+ } else if (m_seekable) {
+ m_availablePlaybackRange = QMediaTimeRange(0, duration());
+ } else {
+ m_availablePlaybackRange = QMediaTimeRange();
+ }
+
+ Q_EMIT availablePlaybackRangesChanged(m_availablePlaybackRange);
+}
+
+qreal QOhPlayerControl::playbackRate() const
+{
+ return m_player->playbackRate();
+}
+
+void QOhPlayerControl::setPlaybackRate(qreal rate)
+{
+ if (m_player->setPlaybackRate(rate)) {
+ emit playbackRateChanged(rate);
+ }
+}
+
+QMediaContent QOhPlayerControl::media() const
+{
+ return m_media;
+}
+
+const QIODevice *QOhPlayerControl::mediaStream() const
+{
+ return m_stream;
+}
+
+void QOhPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream)
+{
+ if (m_media == media && m_stream == stream)
+ return;
+
+ m_media = media;
+ m_stream = stream;
+ if (media.isNull()) {
+ m_status = QMediaPlayer::NoMedia;
+ } else {
+ m_status = QMediaPlayer::LoadingMedia;
+
+ /* 确保 Surface 已设置到 AVPlayer(如果 setVideoRenderOutput 在 setMedia 之后才调用) */
+ if (m_videoRenderControl && m_videoRenderControl->producerWindow())
+ m_player->setDisplay(m_videoRenderControl->producerWindow());
+
+ m_player->setDataSource(media.request());
+ /* 注意:不再调用 m_videoRenderControl->setDataSource()
+ * NativeImage 方案由 AVPlayer Surface 模式直接输出到 ConsumerSurface */
+
+ m_status = QMediaPlayer::LoadedMedia;
+ }
+
+ Q_EMIT mediaChanged(media);
+}
+
+void QOhPlayerControl::play()
+{
+ playOrPause(QMediaPlayer::PlayingState);
+}
+
+void QOhPlayerControl::pause()
+{
+ playOrPause(QMediaPlayer::PausedState);
+}
+
+void QOhPlayerControl::playOrPause(QMediaPlayer::State state)
+{
+ if (m_status == QMediaPlayer::NoMedia || state == QMediaPlayer::StoppedState)
+ return;
+
+ if (m_status == QMediaPlayer::InvalidMedia) {
+ setMedia(m_media, m_stream);
+ if (m_error != QMediaPlayer::NoError)
+ return;
+ }
+
+ if (state == QMediaPlayer::PausedState) {
+ m_player->pause();
+ if (m_videoRenderControl)
+ m_videoRenderControl->pause();
+ } else {
+ if (m_videoRenderControl)
+ m_videoRenderControl->play();
+ m_player->play();
+ }
+ emit stateChanged(state);
+}
+
+void QOhPlayerControl::stop()
+{
+ if (m_videoRenderControl)
+ m_videoRenderControl->stop();
+
+ m_player->stop();
+ emit stateChanged(QMediaPlayer::StoppedState);
+}
+
+QSize QOhPlayerControl::videoSize() const
+{
+ return m_videoSize;
+}
+
+void QOhPlayerControl::setAudioRole(QAudio::Role role)
+{
+ m_player->setAudioRole(role);
+}
+
+void QOhPlayerControl::setCustomAudioRole(const QString &role)
+{
+ m_player->setCustomAudioRole(role);
+}
+
+void QOhPlayerControl::setSeekable(bool seekable)
+{
+ if (m_seekable == seekable)
+ return;
+
+ m_seekable = seekable;
+ Q_EMIT seekableChanged(m_seekable);
+}
+
+void QOhPlayerControl::setAudioAvailable(bool available)
+{
+ if (m_audioAvailable == available)
+ return;
+
+ m_audioAvailable = available;
+ Q_EMIT videoAvailableChanged(m_audioAvailable);
+}
+
+void QOhPlayerControl::setVideoAvailable(bool available)
+{
+ if (m_videoAvailable == available)
+ return;
+
+ m_videoAvailable = available;
+ Q_EMIT videoAvailableChanged(m_videoAvailable);
+}
+
+void QOhPlayerControl::onWindowCreated()
+{
+ m_player->setDisplay(m_videoWindowcControl->nativeWindow());
+}
+
+void QOhPlayerControl::onStateChanged(qint32)
+{
+ emit stateChanged(state());
+}
+
+void QOhPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
+{
+ QSize newSize(width, height);
+
+ if (width == 0 || height == 0 || newSize == m_videoSize)
+ return;
+
+ m_videoSize = newSize;
+}
+
+void QOhPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status)
+{
+ if (m_status == status)
+ return;
+
+ m_status = status;
+
+ Q_EMIT mediaStatusChanged(m_status);
+
+ if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia)
+ Q_EMIT durationChanged(0);
+
+ if (status == QMediaPlayer::EndOfMedia)
+ Q_EMIT positionChanged(position());
+
+ updateBufferStatus();
+}
+
+void QOhPlayerControl::updateBufferStatus()
+{
+ bool bufferFilled = (m_status == QMediaPlayer::BufferedMedia
+ || m_status == QMediaPlayer::BufferingMedia);
+
+ if (m_bufferFilled != bufferFilled) {
+ m_bufferFilled = bufferFilled;
+ Q_EMIT bufferStatusChanged(bufferStatus());
+ }
+}
+
+void QOhPlayerControl::onBufferingChanged(qint32 percent)
+{
+ m_buffering = percent != 100;
+ m_bufferPercent = percent;
+
+ updateAvailablePlaybackRanges();
+
+ if (state() != QMediaPlayer::StoppedState)
+ setMediaStatus(m_buffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
+}
+
+void QOhPlayerControl::onError(int code, const QString &errorString)
+{
+ qWarning() <<"open harmony player error: " << code << errorString;
+ // Todo 鸿蒙errorCode和QMediaPlayer::Error对应
+ QMediaPlayer::Error mError = QMediaPlayer::ResourceError;
+ switch(code) {
+ case AV_ERR_SERVICE_DIED:
+ mError = QMediaPlayer::ServiceMissingError;
+ break;
+ case AV_ERR_INVALID_VAL:
+ mError = QMediaPlayer::ResourceError;
+ break;
+ default:
+ mError = QMediaPlayer::ResourceError;
+ break;
+ }
+
+ emit error(mError, errorString);
+}
new file mode 100644
@@ -0,0 +1,101 @@
+#ifndef QOHPLAYERCONTROL_H
+#define QOHPLAYERCONTROL_H
+
+#include "qmediacontent.h"
+#include "qmediaplayercontrol.h"
+
+#include <QSize>
+#include <QtCore/qcoreevent.h>
+
+QT_BEGIN_NAMESPACE
+class QOhPlayer;
+class QOhVideoRendererControl;
+class QOhWindowControl;
+
+class QOhPlayerControl : public QMediaPlayerControl
+{
+ Q_OBJECT
+public:
+ QOhPlayerControl(QObject *parent = nullptr);
+ ~QOhPlayerControl() override;
+
+ void setVideoRenderOutput(QOhVideoRendererControl *control);
+ void setVideoWindowOutput(QOhWindowControl *control);
+
+ QMediaPlayer::State state() const override;
+
+ QMediaPlayer::MediaStatus mediaStatus() const override;
+
+ qint64 duration() const override;
+
+ qint64 position() const override;
+ void setPosition(qint64 position) override;
+
+ int volume() const override;
+ void setVolume(int volume) override;
+
+ bool isMuted() const override;
+ void setMuted(bool muted) override;
+
+ int bufferStatus() const override;
+
+ bool isAudioAvailable() const override;
+ bool isVideoAvailable() const override;
+
+ bool isSeekable() const override;
+
+ QMediaTimeRange availablePlaybackRanges() const override;
+
+ qreal playbackRate() const override;
+ void setPlaybackRate(qreal rate) override;
+
+ QMediaContent media() const override;
+ const QIODevice *mediaStream() const override;
+ void setMedia(const QMediaContent &media, QIODevice *stream) override;
+
+ void play() override;
+ void pause() override;
+ void stop() override;
+ QSize videoSize() const;
+
+public slots:
+ void setAudioRole(QAudio::Role role);
+ void setCustomAudioRole(const QString &role);
+
+private slots:
+ void setSeekable(bool seekable);
+ void setAudioAvailable(bool available);
+ void setVideoAvailable(bool available);
+ void onWindowCreated();
+ void onStateChanged(qint32);
+ void onVideoSizeChanged(qint32, qint32 height);
+ void setMediaStatus(QMediaPlayer::MediaStatus status);
+ void onBufferingChanged(qint32 percent);
+ void onError(int code, const QString &errorString);
+private:
+ void updateAvailablePlaybackRanges();
+ void updateBufferStatus();
+ void playOrPause(QMediaPlayer::State state);
+ QOhVideoRendererControl *m_videoRenderControl = nullptr;
+ QOhWindowControl *m_videoWindowcControl = nullptr;
+ QOhPlayer *m_player = nullptr;
+ QMediaPlayer::MediaStatus m_status = QMediaPlayer::NoMedia;
+ QMediaPlayer::Error m_error = QMediaPlayer::NoError;
+ QMediaTimeRange m_availablePlaybackRange;
+ QIODevice *m_stream = nullptr;
+ int m_volume = 100;
+ bool m_muted = false;
+ QMediaContent m_media;
+ QString m_errorString;
+ QSize m_videoSize;
+ bool m_seekable = true;
+ bool m_audioAvailable = false;
+ bool m_videoAvailable = false;
+ bool m_buffering = false;
+ bool m_bufferPercent = 0;
+ bool m_bufferFilled = false;
+};
+
+QT_END_NAMESPACE
+
+#endif
new file mode 100644
@@ -0,0 +1,87 @@
+#include "qvideorenderercontrol.h"
+#include "qohvideorenderercontrol.h"
+#include "qohplayerservice.h"
+#include "qohplayercontrol.h"
+#include "qohwindowcontrol.h"
+#include "qohaudiorolecontrol.h"
+#include "qohcustomaudiorolecontrol.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhPlayerService::QOhPlayerService(QObject *parent)
+ : QMediaService(parent)
+{
+ m_playerControl = new QOhPlayerControl(this);
+
+ m_audioRoleControl = new QOhAudioRoleControl;
+ m_customAudioRoleControl = new QOhCustomAudioRoleControl;
+ connect(m_audioRoleControl, &QOhAudioRoleControl::audioRoleChanged,
+ m_playerControl, &QOhPlayerControl::setAudioRole);
+ connect(m_customAudioRoleControl, &QOhCustomAudioRoleControl::customAudioRoleChanged,
+ m_playerControl, &QOhPlayerControl::setCustomAudioRole);
+}
+
+QOhPlayerService::~QOhPlayerService()
+{
+ if (m_playerControl) {
+ delete m_playerControl;
+ m_playerControl = nullptr;
+ }
+
+ if (m_videoWindowControl) {
+ delete m_videoWindowControl;
+ m_videoWindowControl = nullptr;
+ }
+
+ if (m_videoRenderControl) {
+ delete m_videoRenderControl;
+ m_videoRenderControl = nullptr;
+ }
+}
+
+QMediaControl *QOhPlayerService::requestControl(const char *name)
+{
+ if (0 == qstrcmp(QMediaPlayerControl_iid, name))
+ return m_playerControl;
+
+ if (0 == qstrcmp(QVideoWindowControl_iid, name)) {
+ m_videoWindowControl = new QOhWindowControl();
+ m_videoWindowControl->setPlayerControl(m_playerControl);
+ m_playerControl->setVideoWindowOutput(m_videoWindowControl);
+ return m_videoWindowControl;
+ }
+
+ if (0 == qstrcmp(QVideoRendererControl_iid, name)) {
+ m_videoRenderControl = new QOhVideoRendererControl();
+ m_playerControl->setVideoRenderOutput(m_videoRenderControl);
+ return m_videoRenderControl;
+ }
+
+ if (0 == qstrcmp(QAudioRoleControl_iid, name)){
+ return m_audioRoleControl;
+ }
+
+ if (0 == qstrcmp(QCustomAudioRoleControl_iid, name)){
+ return m_customAudioRoleControl;
+ }
+
+ return nullptr;
+}
+
+void QOhPlayerService::releaseControl(QMediaControl *control)
+{
+ if (m_videoWindowControl == control) {
+ delete m_videoWindowControl;
+ m_videoWindowControl = nullptr;
+ m_playerControl->setVideoWindowOutput(nullptr);
+ }
+
+ if (m_videoRenderControl == control) {
+ m_videoRenderControl->stop();
+ delete m_videoRenderControl;
+ m_videoRenderControl = nullptr;
+ m_playerControl->setVideoRenderOutput(nullptr);
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,34 @@
+#ifndef QOHPLAYERSERVICE_H
+#define QOHPLAYERSERVICE_H
+
+#include "qmediaservice.h"
+
+QT_BEGIN_NAMESPACE
+
+class QOhVideoRendererControl;
+class QOhPlayerControl;
+class QOhWindowControl;
+class QOhAudioRoleControl;
+class QOhCustomAudioRoleControl;
+
+class QOhPlayerService : public QMediaService
+{
+ Q_OBJECT
+public:
+ QOhPlayerService(QObject *parent = nullptr);
+ ~QOhPlayerService() override;
+
+ QMediaControl *requestControl(const char *name) override;
+ void releaseControl(QMediaControl *control) override;
+
+private:
+ QOhPlayerControl *m_playerControl = nullptr;
+ QOhVideoRendererControl *m_videoRenderControl = nullptr;
+ QOhWindowControl *m_videoWindowControl = nullptr;
+ QOhAudioRoleControl *m_audioRoleControl = nullptr;
+ QOhCustomAudioRoleControl *m_customAudioRoleControl = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
new file mode 100644
@@ -0,0 +1,270 @@
+#include <QDebug>
+#include <QVideoSurfaceFormat>
+#include <QAbstractVideoSurface>
+#include <QVideoFrame>
+#include <unistd.h>
+#include <poll.h>
+#include <cerrno>
+#include <native_buffer/native_buffer.h>
+#include <native_buffer/buffer_common.h>
+#include <native_window/external_window.h>
+
+#include "qohnativevideobuffer.h"
+#include "qohvideorenderercontrol.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhVideoRendererControl::QOhVideoRendererControl(QObject *parent)
+ : QVideoRendererControl(parent)
+{
+ m_nativeImage = OH_ConsumerSurface_Create();
+ if (!m_nativeImage) {
+ qCritical() << "QOhVideoRendererControl: OH_ConsumerSurface_Create failed";
+ return;
+ }
+
+ /* 必须设置 CPU_READ usage,否则 OH_NativeBuffer_Map 返回的是图形内存句柄而非像素数据 */
+ int32_t usageRet = OH_ConsumerSurface_SetDefaultUsage(
+ m_nativeImage,
+ NATIVEBUFFER_USAGE_CPU_READ | NATIVEBUFFER_USAGE_CPU_WRITE | NATIVEBUFFER_USAGE_MEM_DMA);
+ if (usageRet != 0)
+ qWarning() << "QOhVideoRendererControl: SetDefaultUsage failed, ret=" << usageRet;
+
+ m_producerWindow = OH_NativeImage_AcquireNativeWindow(m_nativeImage);
+ if (!m_producerWindow) {
+ qCritical() << "QOhVideoRendererControl: OH_NativeImage_AcquireNativeWindow failed";
+ return;
+ }
+
+ OH_OnFrameAvailableListener listener;
+ listener.context = this;
+ listener.onFrameAvailable = &QOhVideoRendererControl::onFrameAvailable;
+ int ret = OH_NativeImage_SetOnFrameAvailableListener(m_nativeImage, listener);
+ if (ret != 0)
+ qWarning() << "QOhVideoRendererControl: SetOnFrameAvailableListener failed, ret=" << ret;
+}
+
+QOhVideoRendererControl::~QOhVideoRendererControl()
+{
+ stop();
+
+ if (m_nativeImage) {
+ OH_NativeImage_Destroy(&m_nativeImage);
+ m_nativeImage = nullptr;
+ m_producerWindow = nullptr;
+ }
+}
+
+OHNativeWindow *QOhVideoRendererControl::producerWindow() const
+{
+ return m_producerWindow;
+}
+
+void QOhVideoRendererControl::play()
+{
+ m_active = true;
+}
+
+void QOhVideoRendererControl::stop()
+{
+ m_active = false;
+
+ if (m_surface && m_surface->isActive())
+ m_surface->stop();
+
+ m_pixelFormat = QVideoFrame::Format_Invalid;
+ m_surfacePixelFormat = QVideoFrame::Format_Invalid;
+ m_frameSize = QSize();
+}
+
+void QOhVideoRendererControl::pause()
+{
+ m_active = false;
+}
+
+QAbstractVideoSurface *QOhVideoRendererControl::surface() const
+{
+ return m_surface;
+}
+
+void QOhVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
+{
+ if (m_surface == surface)
+ return;
+
+ if (m_surface && m_surface->isActive())
+ m_surface->stop();
+
+ m_surface = surface;
+ m_pixelFormat = QVideoFrame::Format_Invalid;
+ m_surfacePixelFormat = QVideoFrame::Format_Invalid;
+ m_frameSize = QSize();
+}
+
+void QOhVideoRendererControl::onFrameAvailable(void *context)
+{
+ QOhVideoRendererControl *ctrl = static_cast<QOhVideoRendererControl *>(context);
+ if (!ctrl)
+ return;
+
+ QMetaObject::invokeMethod(ctrl, "consumeFrame", Qt::QueuedConnection);
+}
+
+void QOhVideoRendererControl::consumeFrame()
+{
+ if (!m_active || !m_surface || !m_nativeImage)
+ return;
+
+ OHNativeWindowBuffer *windowBuffer = nullptr;
+ int fenceFd = -1;
+ int ret = OH_NativeImage_AcquireNativeWindowBuffer(m_nativeImage, &windowBuffer, &fenceFd);
+ if (ret != 0 || !windowBuffer) {
+ qWarning() << "QOhVideoRendererControl: AcquireNativeWindowBuffer failed, ret=" << ret;
+ return;
+ }
+
+ OH_NativeBuffer *nativeBuffer = nullptr;
+ OH_NativeBuffer_FromNativeWindowBuffer(windowBuffer, &nativeBuffer);
+ if (!nativeBuffer) {
+ qWarning() << "QOhVideoRendererControl: OH_NativeBuffer_FromNativeWindowBuffer failed";
+ OH_NativeImage_ReleaseNativeWindowBuffer(m_nativeImage, windowBuffer, fenceFd);
+ return;
+ }
+
+ if (fenceFd >= 0) {
+ struct pollfd pollfds = {};
+ pollfds.fd = fenceFd;
+ pollfds.events = POLLIN;
+
+ int retCode = -1;
+ constexpr int MAX_RETRY = 3;
+ int retryCount = 0;
+
+ do {
+ retCode = poll(&pollfds, 1, 3000); // 3 秒超时
+ retryCount++;
+
+ if (retryCount >= MAX_RETRY) {
+ qWarning() << "QOhVideoRendererControl: poll fence reached max retry, ret=" << retCode
+ << "errno=" << errno;
+ break;
+ }
+ } while (retCode == -1 && (errno == EINTR || errno == EAGAIN));
+
+ close(fenceFd);
+ fenceFd = -1;
+
+ if (retCode <= 0) {
+ qWarning() << "QOhVideoRendererControl: poll fence failed, ret=" << retCode;
+ OH_NativeImage_ReleaseNativeWindowBuffer(m_nativeImage, windowBuffer, -1);
+ return;
+ }
+ }
+
+ void *ptr = nullptr;
+ int ret2 = OH_NativeBuffer_Map(nativeBuffer, &ptr);
+ if (ret2 != 0 || !ptr) {
+ qWarning() << "QOhVideoRendererControl: OH_NativeBuffer_Map failed, ret=" << ret2;
+ OH_NativeImage_ReleaseNativeWindowBuffer(m_nativeImage, windowBuffer, -1);
+ return;
+ }
+
+ OH_NativeBuffer_Config config{};
+ OH_NativeBuffer_GetConfig(nativeBuffer, &config);
+
+ if (config.width <= 0 || config.height <= 0) {
+ qWarning() << "QOhVideoRendererControl: invalid buffer config w=" << config.width
+ << " h=" << config.height;
+ OH_NativeBuffer_Unmap(nativeBuffer);
+ OH_NativeImage_ReleaseNativeWindowBuffer(m_nativeImage, windowBuffer, -1);
+ return;
+ }
+
+ const OH_NativeBuffer_Format cfgFmt = static_cast<OH_NativeBuffer_Format>(config.format);
+
+ const int safeMax = config.stride * config.height;
+ int strideBytes = config.stride;
+ int dataSize;
+ switch (cfgFmt) {
+ case NATIVEBUFFER_PIXEL_FMT_YCBCR_420_SP:
+ case NATIVEBUFFER_PIXEL_FMT_YCRCB_420_SP:
+ case NATIVEBUFFER_PIXEL_FMT_YCBCR_420_P:
+ case NATIVEBUFFER_PIXEL_FMT_YCRCB_420_P:
+ dataSize = config.stride * config.height * 3 / 2;
+ break;
+ default:
+ dataSize = safeMax;
+ break;
+ }
+ (void)safeMax;
+ uchar *dataCopy = new uchar[dataSize];
+ memcpy(dataCopy, ptr, dataSize);
+
+ OH_NativeBuffer_Unmap(nativeBuffer);
+ OH_NativeImage_ReleaseNativeWindowBuffer(m_nativeImage, windowBuffer, -1);
+ QVideoFrame::PixelFormat qtFmt = nativeFormatToQtFormat(cfgFmt);
+
+ auto *videoBuffer = new QOhNativeVideoBuffer(dataCopy, dataSize, config, strideBytes);
+ QVideoFrame frame(videoBuffer, QSize(config.width, config.height), qtFmt);
+ if (!m_surface->isActive()
+ || m_frameSize != frame.size()
+ || m_pixelFormat != qtFmt) {
+ startSurface(frame);
+ }
+
+ if (!m_surface->isActive())
+ return;
+
+ if (m_surfacePixelFormat != qtFmt) {
+ QImage argb = frame.image();
+ if (argb.isNull()) {
+ qWarning() << "QOhVideoRendererControl: frame.image() failed, fmt=" << qtFmt;
+ return;
+ }
+ m_surface->present(QVideoFrame(argb));
+ } else {
+ m_surface->present(frame);
+ }
+}
+
+void QOhVideoRendererControl::startSurface(const QVideoFrame &frame)
+{
+ if (!m_surface)
+ return;
+
+ if (m_surface->isActive())
+ m_surface->stop();
+
+ QVideoSurfaceFormat format(frame.size(), frame.pixelFormat());
+ if (!m_surface->isFormatSupported(format)) {
+ format = QVideoSurfaceFormat(frame.size(), QVideoFrame::Format_ARGB32);
+ }
+
+ if (!m_surface->start(format)) {
+ qWarning() << "QOhVideoRendererControl: failed to start surface with format" << format;
+ return;
+ }
+
+ m_frameSize = frame.size();
+ m_pixelFormat = frame.pixelFormat(); /* 记录原始帧格式 */
+ m_surfacePixelFormat = format.pixelFormat(); /* 记录 surface 实际启动格式 */
+}
+
+QVideoFrame::PixelFormat QOhVideoRendererControl::nativeFormatToQtFormat(OH_NativeBuffer_Format fmt)
+{
+ switch (fmt) {
+ case NATIVEBUFFER_PIXEL_FMT_YCBCR_420_SP: return QVideoFrame::Format_NV12;
+ case NATIVEBUFFER_PIXEL_FMT_YCRCB_420_SP: return QVideoFrame::Format_NV21;
+ case NATIVEBUFFER_PIXEL_FMT_RGBA_8888: return QVideoFrame::Format_ARGB32; /* RGBA 内存[R,G,B,A], Qt ARGB32小端 = [B,G,R,A],小端 ARM 下指向同一内存内容 */
+ case NATIVEBUFFER_PIXEL_FMT_RGBX_8888: return QVideoFrame::Format_RGB32;
+ case NATIVEBUFFER_PIXEL_FMT_BGRA_8888: return QVideoFrame::Format_ARGB32; /* BGRA 内存小端 = Qt ARGB32 */
+ case NATIVEBUFFER_PIXEL_FMT_YCBCR_420_P: return QVideoFrame::Format_YUV420P;
+ case NATIVEBUFFER_PIXEL_FMT_YCRCB_420_P: return QVideoFrame::Format_YUV420P;
+ default:
+ qWarning() << "QOhVideoRendererControl: unknown native format" << static_cast<int>(fmt)
+ << ", fallback to ARGB32";
+ return QVideoFrame::Format_ARGB32;
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,53 @@
+#ifndef QOHVIDEORENDERERCONTROL_H
+#define QOHVIDEORENDERERCONTROL_H
+
+#include <QList>
+#include <QSize>
+#include <QVideoFrame>
+#include <QVideoRendererControl>
+#include <native_image/native_image.h>
+#include <native_window/external_window.h>
+#include <native_buffer/native_buffer.h>
+#include <native_buffer/buffer_common.h>
+#include <atomic>
+
+QT_BEGIN_NAMESPACE
+
+class QOhVideoRendererControl : public QVideoRendererControl
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QOhVideoRendererControl)
+
+public:
+ explicit QOhVideoRendererControl(QObject *parent = nullptr);
+ ~QOhVideoRendererControl();
+
+ OHNativeWindow *producerWindow() const;
+
+ void play();
+ void stop();
+ void pause();
+
+ QAbstractVideoSurface *surface() const override;
+ void setSurface(QAbstractVideoSurface *surface) override;
+
+private slots:
+ void consumeFrame();
+
+private:
+ static void onFrameAvailable(void *context);
+ void startSurface(const QVideoFrame &frame);
+ static QVideoFrame::PixelFormat nativeFormatToQtFormat(OH_NativeBuffer_Format fmt);
+
+ QAbstractVideoSurface *m_surface = nullptr;
+ OH_NativeImage *m_nativeImage = nullptr;
+ OHNativeWindow *m_producerWindow = nullptr;
+ bool m_active = false;
+ QVideoFrame::PixelFormat m_pixelFormat = QVideoFrame::Format_Invalid; /* 原始帧格式 */
+ QVideoFrame::PixelFormat m_surfacePixelFormat = QVideoFrame::Format_Invalid; /* surface 实际启动格式 */
+ QSize m_frameSize;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHVIDEORENDERERCONTROL_H
new file mode 100644
@@ -0,0 +1,206 @@
+#include "qohwindowcontrol.h"
+#include <qopenharmonydefines.h>
+#include <QHash>
+#include "qohplayercontrol.h"
+#include <arkui/native_node.h>
+#include <arkui/native_type.h>
+#include <arkui/native_interface.h>
+#include <native_window/external_window.h>
+#include <QTimer>
+#include <QWidget>
+#include <uv.h>
+
+#include <QtPlatformHeaders/qohwindowfunctions.h>
+
+struct Node {
+ ArkUI_NodeType nodeType; // Type of the node. The value can be ARKUI_NODE_XCOMPONENT or other types supported in later versions.
+ ArkUI_NodeHandle node; // ARKUI_NODE_XCOMPONENT type or other.
+ ArkUI_NodeHandle container; // Must be of type ARKUI_NODE_STACK.
+ char nodeOwner[8]; // Describes the body of the Node structure to be created, such as "QT," "CEF," "SDL2," "FLUT".
+ void* nodePrivate; // Structure used for the definition of the nodeOwner body record itself.
+};
+
+struct WindowNode : Node
+{
+ void *window;
+};
+
+class VideoOutWidget : public QWidget
+{
+ // 阻止Qt绘制,创建surface
+public:
+ VideoOutWidget(QWidget *parent) : QWidget(parent)
+ {
+ setUpdatesEnabled(false);
+ setObjectName("_q_oh_videowidget");
+ //setAttribute(Qt::WA_UpdatesDisabled);
+ //setAttribute(Qt::WA_StyledBackground);
+ }
+};
+
+
+QOhWindowControl::QOhWindowControl(QObject *parent)
+ : QVideoWindowControl(parent)
+ , m_timer(nullptr)
+ , m_widget(nullptr)
+{
+
+}
+
+QOhWindowControl::~QOhWindowControl()
+{
+
+}
+
+WId QOhWindowControl::winId() const
+{
+ return m_windowId;
+}
+
+void QOhWindowControl::setWinId(WId id)
+{
+ if (m_windowId == id)
+ return;
+ m_windowId = id;
+
+ QWidget *widget = QWidget::find(m_windowId);
+ if (widget == nullptr) {
+ qWarning() << "set player output widget failed";
+ return;
+ }
+
+ m_widget = new VideoOutWidget(widget);
+ WId videoId = m_widget->winId();
+ m_widget->show();
+ QOhWindowFunctions::setNativeNodeRenderFit(m_widget->windowHandle(), QOhWindowFunctions::NativeNodeRenderFit::FIT_RESIZE_FILL);
+ WindowNode *node = reinterpret_cast<WindowNode*>(videoId);
+ if (node == nullptr) {
+ qWarning() << "set player output widget failed";
+ return;
+ }
+
+ if (node->window != nullptr) {
+ setNativeWindow(reinterpret_cast<OHNativeWindow *>(node->window));
+ } else {
+ if (m_timer == nullptr) {
+ m_timer = new QTimer(this);
+ connect(m_timer, &QTimer::timeout, this, [node, this]{
+ if (node->window != nullptr) {
+ setNativeWindow(reinterpret_cast<OHNativeWindow *>(node->window));
+ m_timer->stop();
+ }
+ });
+ }
+ if (!m_timer->isActive()) {
+ m_timer->start(500);
+ }
+ }
+}
+
+QRect QOhWindowControl::displayRect() const
+{
+ return m_displayRect;
+}
+
+void QOhWindowControl::setDisplayRect(const QRect &rect)
+{
+ m_displayRect = rect;
+ if (m_widget != nullptr)
+ m_widget->setGeometry(0, 0, rect.width(), rect.height());
+}
+
+bool QOhWindowControl::isFullScreen() const
+{
+ return m_fullScreen;
+}
+
+void QOhWindowControl::setFullScreen(bool fullScreen)
+{
+ emit fullScreenChanged(m_fullScreen = fullScreen);
+}
+
+void QOhWindowControl::repaint()
+{
+
+}
+
+QSize QOhWindowControl::nativeSize() const
+{
+ return m_playerControl == nullptr ? QSize() : m_playerControl->videoSize();
+}
+
+Qt::AspectRatioMode QOhWindowControl::aspectRatioMode() const
+{
+ return m_aspectRatioMode;
+}
+
+void QOhWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
+{
+ m_aspectRatioMode = mode;
+}
+
+int QOhWindowControl::brightness() const
+{
+ return m_brightness;
+}
+
+void QOhWindowControl::setBrightness(int brightness)
+{
+ m_brightness = brightness;
+
+ emit brightnessChanged(brightness);
+}
+
+int QOhWindowControl::contrast() const
+{
+ return m_contrast;
+}
+
+void QOhWindowControl::setContrast(int contrast)
+{
+ m_contrast = contrast;
+
+ emit contrastChanged(contrast);
+}
+
+int QOhWindowControl::hue() const
+{
+ return m_hue;
+}
+
+void QOhWindowControl::setHue(int hue)
+{
+ m_hue = hue;
+
+ emit hueChanged(hue);
+}
+
+int QOhWindowControl::saturation() const
+{
+ return m_saturation;
+}
+
+void QOhWindowControl::setSaturation(int saturation)
+{
+ m_saturation = saturation;
+
+ emit saturationChanged(saturation);
+}
+
+void QOhWindowControl::setNativeWindow(OHNativeWindow *nativeWindow)
+{
+ m_nativeWindow = nativeWindow;
+ QMetaObject::invokeMethod(this, "windowCreated");
+}
+
+OHNativeWindow *QOhWindowControl::nativeWindow() const
+{
+ return m_nativeWindow;
+}
+
+void QOhWindowControl::setPlayerControl(QOhPlayerControl *control)
+{
+ if (control == nullptr)
+ return;
+ m_playerControl = control;
+}
new file mode 100644
@@ -0,0 +1,70 @@
+#ifndef QOHWINDOWCONTROL_H
+#define QOHWINDOWCONTROL_H
+
+#include "qvideowindowcontrol.h"
+#include <multimedia/player_framework/avplayer.h>
+
+QT_BEGIN_NAMESPACE
+class VideoOutWidget;
+class QOhPlayerControl;
+class QOhWindowControl : public QVideoWindowControl
+{
+ Q_OBJECT
+public:
+ QOhWindowControl(QObject *parent = nullptr);
+ ~QOhWindowControl() override;
+
+ WId winId() const override;
+ void setWinId(WId id) override;
+
+ QRect displayRect() const override;
+ void setDisplayRect(const QRect &rect) override;
+
+ bool isFullScreen() const override;
+ void setFullScreen(bool fullScreen) override;
+
+ void repaint() override;
+
+ QSize nativeSize() const override;
+
+ Qt::AspectRatioMode aspectRatioMode() const override;
+ void setAspectRatioMode(Qt::AspectRatioMode mode) override;
+
+ int brightness() const override;
+ void setBrightness(int brightness) override;
+
+ int contrast() const override;
+ void setContrast(int contrast) override;
+
+ int hue() const override;
+ void setHue(int hue) override;
+
+ int saturation() const override;
+ void setSaturation(int saturation) override;
+
+ void setNativeWindow(OHNativeWindow *nativeWindow);
+ OHNativeWindow *nativeWindow() const;
+
+ void setPlayerControl(QOhPlayerControl *control);
+
+Q_SIGNALS:
+ void windowCreated();
+
+private:
+ WId m_windowId = 0;
+ OHNativeWindow *m_nativeWindow = nullptr;
+ Qt::AspectRatioMode m_aspectRatioMode = Qt::KeepAspectRatio;
+ QRect m_displayRect;
+ int m_brightness = 0;
+ int m_contrast = 0;
+ int m_hue = 0;
+ int m_saturation = 0;
+ bool m_fullScreen = false;
+ QOhPlayerControl *m_playerControl;
+ QTimer *m_timer;
+ VideoOutWidget *m_widget;
+};
+
+QT_END_NAMESPACE
+
+#endif
new file mode 100644
@@ -0,0 +1,100 @@
+#include <QLoggingCategory>
+
+#include "qcamera.h"
+#include "qmediaserviceproviderplugin.h"
+#include "qohmediaserviceplugin.h"
+#include "mediacapture/qohcamerasession.h"
+#include "mediacapture/qohcaptureservice.h"
+#include "mediacapture/qohcamerainfocontrol.h"
+#include "qohplayerservice.h"
+// #include "qopenharmonyaudioservice.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qtOPenHaronyMediaPlugin, "qt.multimedia.plugins.openharmony")
+
+QOhMediaServicePlugin::QOhMediaServicePlugin()
+{
+}
+
+QOhMediaServicePlugin::~QOhMediaServicePlugin()
+{
+}
+
+QMediaService *QOhMediaServicePlugin::create(const QString &key)
+{
+ if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
+ return new QOhPlayerService();
+
+ if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) {
+ return new QOhCaptureService();
+ }
+
+ qCWarning(qtOPenHaronyMediaPlugin) << "OPenHarony service plugin: unsupported key:" << key;
+ return 0;
+}
+
+void QOhMediaServicePlugin::release(QMediaService *service)
+{
+ delete service;
+}
+
+QMediaServiceProviderHint::Features QOhMediaServicePlugin::supportedFeatures(const QByteArray &service) const
+{
+ if (service == Q_MEDIASERVICE_MEDIAPLAYER)
+ return QMediaServiceProviderHint::VideoSurface;
+
+ if (service == Q_MEDIASERVICE_CAMERA)
+ return QMediaServiceProviderHint::VideoSurface | QMediaServiceProviderHint::RecordingSupport;
+
+
+ return QMediaServiceProviderHint::Features();
+}
+
+QByteArray QOhMediaServicePlugin::defaultDevice(const QByteArray &service) const
+{
+ if (service == Q_MEDIASERVICE_CAMERA && !QOhCameraSession::availableCameras().isEmpty())
+ return QOhCameraSession::availableCameras().first().name;
+
+ return QByteArray();
+}
+
+QList<QByteArray> QOhMediaServicePlugin::devices(const QByteArray &service) const
+{
+ Q_UNUSED(service);
+ if (service == Q_MEDIASERVICE_CAMERA) {
+ QList<QByteArray> devices;
+ const QList<OhCameraInfo> &cameras = QOhCameraSession::availableCameras();
+ for (int i = 0; i < cameras.count(); ++i)
+ devices.append(cameras.at(i).name);
+ return devices;
+ }
+
+ return QList<QByteArray>();
+}
+
+QString QOhMediaServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
+{
+ if (service == Q_MEDIASERVICE_CAMERA) {
+ const QList<OhCameraInfo> &cameras = QOhCameraSession::availableCameras();
+ for (int i = 0; i < cameras.count(); ++i) {
+ const OhCameraInfo &info = cameras.at(i);
+ if (info.name == device)
+ return info.description;
+ }
+ }
+
+ return QString();
+}
+
+QCamera::Position QOhMediaServicePlugin::cameraPosition(const QByteArray &device) const
+{
+ return QOhCameraInfoControl::position(device);
+}
+
+int QOhMediaServicePlugin::cameraOrientation(const QByteArray &device) const
+{
+ return QOhCameraInfoControl::orientation(device);
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,42 @@
+#ifndef QOHMEDIASERVICEPLUGIN_H
+#define QOHMEDIASERVICEPLUGIN_H
+
+#include <QMediaServiceProviderPlugin>
+
+QT_BEGIN_NAMESPACE
+
+class QOhMediaServicePlugin
+ : public QMediaServiceProviderPlugin
+ , public QMediaServiceSupportedDevicesInterface
+ , public QMediaServiceDefaultDeviceInterface
+ , public QMediaServiceCameraInfoInterface
+ , public QMediaServiceFeaturesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+ Q_INTERFACES(QMediaServiceDefaultDeviceInterface)
+ Q_INTERFACES(QMediaServiceCameraInfoInterface)
+ Q_INTERFACES(QMediaServiceFeaturesInterface)
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0"
+ FILE "oh_mediaservice.json")
+
+public:
+ QOhMediaServicePlugin();
+ ~QOhMediaServicePlugin();
+
+ QMediaService* create(QString const& key) override;
+ void release(QMediaService *service) override;
+
+ QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override;
+
+ QByteArray defaultDevice(const QByteArray &service) const override;
+ QList<QByteArray> devices(const QByteArray &service) const override;
+ QString deviceDescription(const QByteArray &service, const QByteArray &device) override;
+
+ QCamera::Position cameraPosition(const QByteArray &device) const override;
+ int cameraOrientation(const QByteArray &device) const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHMEDIASERVICEPLUGIN_H
new file mode 100644
@@ -0,0 +1,26 @@
+include (wrappers/napi/napi.pri)
+include (mediacapture/mediacapture.pri)
+include (player/player.pri)
+
+TARGET = qtmedia_openharmony
+
+LIBS += -lace_ndk.z -lavplayer -lrawfile.z -lnative_media_core
+LIBS += -lohcamera -lohimage -limage_receiver -lnative_image -lnative_buffer
+LIBS += -lnative_media_codecbase -lnative_media_core -lnative_media_vdec \
+ -lnative_media_avsource -lnative_media_avdemuxer -lohaudio -lnative_window
+
+QT += multimedia-private core-private network
+
+HEADERS += \
+ qohmediaserviceplugin.h
+
+SOURCES += \
+ qohmediaserviceplugin.cpp
+
+
+OTHER_FILES += \
+ oh_mediaservice.json
+
+PLUGIN_TYPE = mediaservice
+PLUGIN_CLASS_NAME = QOPenHarmonyMediaServicePlugin
+load(qt_plugin)
new file mode 100644
@@ -0,0 +1,11 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ # $$PWD/ohcamera.h \
+ $$PWD/ohmediarecorder.h \
+ $$PWD/ohmultimediautils.h
+
+SOURCES += \
+ # $$PWD/ohcamera.cpp \
+ $$PWD/ohmediarecorder.cpp \
+ $$PWD/ohmultimediautils.cpp
new file mode 100644
@@ -0,0 +1,167 @@
+#include <QDebug>
+#include <QThread>
+#include <QWriteLocker>
+#include <QReadWriteLock>
+#include <QSharedPointer>
+#include <QtCore/QJsonValue>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonParseError>
+#include <QtCore/QLoggingCategory>
+
+#include "QtCore/QOpenHarmonyJsObject"
+#include "QtCore/QOpenHarmonyJsEnvironment"
+#include "QtCore/QOpenHarmonyJsObjectLoader"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qtCameraInfo, "qt.multimedia.plugins.openharmony.camerainfo")
+
+using CameraMap = QHash<QString, OhCamera *>;
+
+Q_GLOBAL_STATIC(CameraMap, cameras)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
+
+class OhCameraPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ OhCameraPrivate();
+ ~OhCameraPrivate();
+
+ Q_INVOKABLE bool init(const QString &cameraId);
+
+ static QStringList getIdOfCameras();
+ static void getCameraInfo(const QString &id, OhCameraInfo *info);
+public:
+ QString m_cameraId;
+ static QSharedPointer<QOpenHarmonyJsObject> m_jsCamera;
+};
+QSharedPointer<QOpenHarmonyJsObject> OhCameraPrivate::m_jsCamera(Q_NULLPTR);
+
+OhCameraPrivate::OhCameraPrivate()
+ : QObject()
+{
+ m_jsCamera = qJsObjectLoader->create("JsCameraManager");
+}
+
+OhCameraPrivate::~OhCameraPrivate()
+{
+
+}
+
+bool OhCameraPrivate::init(const QString &cameraId)
+{
+ Q_UNUSED(cameraId);
+ return false;
+}
+
+QStringList OhCameraPrivate::getIdOfCameras()
+{
+ if (m_jsCamera.isNull()){
+ m_jsCamera = qJsObjectLoader->create("JsCameraManager");
+ }
+ QStringList ids = m_jsCamera->call<QStringList>("idOfCameras");
+ return ids;
+}
+
+void OhCameraPrivate::getCameraInfo(const QString &id, OhCameraInfo *info)
+{
+ Q_ASSERT(info);
+ if (m_jsCamera.isNull()){
+ m_jsCamera = qJsObjectLoader->create("JsCameraManager");
+ }
+
+ QString json = m_jsCamera->call<QString>("cameraInfo", id);
+ if (json.isEmpty())
+ return;
+
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(json.toLocal8Bit(), &error);
+ if (QJsonParseError::NoError != error.error) {
+ qCWarning(qtCameraInfo) << "parase camera info from openharmony: " << error.errorString();
+ return;
+ }
+
+ QJsonObject obj = doc.object();
+ QString cameraId = obj.value(QStringLiteral("cameraId")).toString();
+ info->name = cameraId.toLocal8Bit();
+ int pos = obj.value(QStringLiteral("cameraPosition")).toInt();
+ info->position = QCamera::Position(pos);
+
+ static QHash<int, QString> sCameraTypeDes{
+ { 3, QStringLiteral("Telephoto camera") },
+ { 1, QStringLiteral("Wide Angle Len Camera") },
+ { 2, QStringLiteral("Ultra wide Angle camera") },
+ { 0, QStringLiteral("The camera type was not specified") },
+ { 4, QStringLiteral("Camera with depth of field information") }
+ };
+
+ static QHash<int, QString> sConnectionTypeDes{
+ { 0, QStringLiteral(" Built-in Camera") },
+ { 1, QStringLiteral(" Usb-connected camera") },
+ { 2, QStringLiteral(" Remotely connected camera") },
+ };
+
+ int cameraType = obj.value("cameraType").toInt();
+ int connectType = obj.value("connectionType").toInt();
+ info->description = sCameraTypeDes.value(cameraType) + sConnectionTypeDes.value(connectType);
+ info->orientation = 0; //TODO Returns the physical orientation of the camera sensor.
+}
+
+OhCamera::~OhCamera()
+{
+
+}
+
+QString OhCamera::cameraId() const
+{
+ Q_D(const OhCamera);
+ return d->m_cameraId;
+}
+
+QStringList OhCamera::getIdOfCameras()
+{
+ return std::move(OhCameraPrivate::getIdOfCameras());
+}
+
+OhCamera *OhCamera::open(const QString &cameraId)
+{
+ OhCameraPrivate *d = new OhCameraPrivate();
+ QThread *worker = new QThread;
+ worker->start();
+ d->moveToThread(worker);
+ connect(worker, &QThread::finished, d, &OhCameraPrivate::deleteLater);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(const QString&, cameraId));
+ if (!ok) {
+ worker->quit();
+ worker->wait(5000);
+ delete worker;
+ return 0;
+ }
+
+ OhCamera *q = new OhCamera(d, worker);
+ QWriteLocker locker(rwLock);
+ cameras->insert(cameraId, q);
+
+ return q;
+}
+
+void OhCamera::getCameraInfo(const QString &id, OhCameraInfo *info)
+{
+ OhCameraPrivate::getCameraInfo(id, info);
+}
+
+OhCamera::OhCamera(OhCameraPrivate *d, QThread *worker) : QObject()
+ , d_ptr(d)
+ , m_worker(worker)
+{
+
+}
+
+QT_END_NAMESPACE
+
+#include "OhCamera.moc"
new file mode 100644
@@ -0,0 +1,43 @@
+#ifndef OHCAMERA_H
+#define OHCAMERA_H
+
+#include <QObject>
+#include <QtMultimedia/qcamera.h>
+
+QT_BEGIN_NAMESPACE
+
+class QThread;
+class OhCameraPrivate;
+
+struct OhCameraInfo
+{
+ QByteArray name;
+ QString description;
+ QCamera::Position position;
+ int orientation;
+};
+Q_DECLARE_TYPEINFO(OhCameraInfo, Q_MOVABLE_TYPE);
+
+class OhCamera : public QObject
+{
+ Q_OBJECT
+
+public:
+ ~OhCamera();
+
+ QString cameraId() const;
+ static QStringList getIdOfCameras();
+ static OhCamera *open(const QString &cameraId);
+ static void getCameraInfo(const QString &id, OhCameraInfo *info);
+
+Q_SIGNALS:
+
+private:
+ OhCameraPrivate *d_ptr;
+ QScopedPointer<QThread> m_worker;
+ Q_DECLARE_PRIVATE(OhCamera)
+ OhCamera(OhCameraPrivate *d, QThread *worker);
+};
+
+QT_END_NAMESPACE
+#endif // OHCAMERA_H
new file mode 100644
@@ -0,0 +1,232 @@
+#include "ohmediarecorder.h"
+#include "qopenharmony.h"
+
+#include <QDebug>
+#include <QJsObject>
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+#include "private/qjspromise_p.h"
+
+OhMediaRecorder::OhMediaRecorder()
+ :QJsModule("@ohos.multimedia.media"),
+ m_audioBitrate(48000),
+ m_audioChannel(2),
+ m_audioCodeC(AAC),
+ m_audioSampleRate(48000),
+ m_videoBitrate(200000),
+ m_videoCodec(AVC),
+ m_videoFrameSize(640, 480),
+ m_videoFrameRate(30)
+{
+ m_mediaRecorder = QtOh::runOnJsUIThreadWithPromise<QJsObject *>([this](auto p){
+ QJsPromise promise(call("createAVRecorder").As<Napi::Promise>());
+ promise.onThen([p](const Napi::CallbackInfo &info){
+ if (info.Length() < 1) {
+ p->set_value(nullptr);
+ return ;
+ }
+
+ Napi::Object recorder = info[0].As<Napi::Object>();
+ p->set_value(new QJsObject(recorder));
+ });
+ });
+}
+
+OhMediaRecorder::~OhMediaRecorder()
+{
+ if(m_mediaRecorder){
+ delete m_mediaRecorder;
+ m_mediaRecorder = nullptr;
+ }
+}
+
+void OhMediaRecorder::release()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ if(m_mediaRecorder)
+ m_mediaRecorder->call("release");
+ });
+}
+
+QString OhMediaRecorder::prepare()
+{
+ if(!m_mediaRecorder)
+ return QString();
+
+ return QtOh::runOnJsUIThreadWithPromise<QString>([this] (auto p){
+ Napi::Object AVRecorderProfile = Napi::Object::New(m_mediaRecorder->env());
+ AVRecorderProfile.Set("audioBitrate", m_audioBitrate);
+ AVRecorderProfile.Set("audioChannels", m_audioChannel);
+
+ QJsModule fileInfo("@ohos.file.fs");
+ QJsObject jsFile = QJsObject(fileInfo.call("openSync", { Napi::String::New(fileInfo.env(), m_outputFile.toStdString()),\
+ Napi::Number::New(fileInfo.env(), 02 | 0100)}).As<Napi::Object>());
+ if (jsFile.object().IsNull() || jsFile.object().IsEmpty()){
+ qDebug() << "openSync fialed:" << jsFile.lastError();
+ p->set_value(QString());
+ }
+
+ int fd = jsFile.get("fd").ToNumber();
+ QString fdStr = QString("fd://%1").arg(fd);
+
+ //TODO 无法构造ts枚举对象,暂时直接使用枚举值
+ switch(m_audioCodeC) {
+ case AAC:
+ AVRecorderProfile.Set("audioCodec", "audio/mp4a-latm");
+ case VORBIS:
+ AVRecorderProfile.Set("audioCodec", "audio/vorbis");
+ case FLAC:
+ AVRecorderProfile.Set("audioCodec", "audio/flac");
+ case MP3:
+ AVRecorderProfile.Set("audioCodec", "audio/mpeg");
+ default:
+ AVRecorderProfile.Set("audioCodec", "audio/mp4a-latm");
+ }
+ AVRecorderProfile.Set("audioSampleRate", m_audioSampleRate);
+
+ AVRecorderProfile.Set("fileFormat", "mp4");
+
+ AVRecorderProfile.Set("videoBitrate", m_videoBitrate);
+ switch(m_videoCodec) {
+ case H263:
+ AVRecorderProfile.Set("videoCodec", "video/h263");
+ case AVC:
+ AVRecorderProfile.Set("videoCodec", "video/avc");
+ case MPEG_2:
+ AVRecorderProfile.Set("videoCodec", "video/mpeg2");
+ case MPEG_4:
+ AVRecorderProfile.Set("videoCodec", "video/mp4v-es");
+ case VP8:
+ AVRecorderProfile.Set("videoCodec", "video/x-vnd.on2.vp8");
+ default:
+ AVRecorderProfile.Set("videoCodec", "video/avc");
+ }
+ AVRecorderProfile.Set("videoFrameWidth", m_videoFrameSize.width());
+ AVRecorderProfile.Set("videoFrameHeight", m_videoFrameSize.height());
+ AVRecorderProfile.Set("videoFrameRate", m_videoFrameRate);
+
+ Napi::Object avRecorderConfig = Napi::Object::New(m_mediaRecorder->env());
+ avRecorderConfig.Set("audioSourceType", 1);
+ avRecorderConfig.Set("videoSourceType", 0);
+ avRecorderConfig.Set("profile", AVRecorderProfile);
+ avRecorderConfig.Set("url", fdStr.toStdString());
+ avRecorderConfig.Set("rotation", 0);
+
+ Napi::Object location = Napi::Object::New(m_mediaRecorder->env());
+ location.Set("latitude", 30);
+ location.Set("longitude", 130);
+ avRecorderConfig.Set("location", location);
+
+ m_mediaRecorder->call("prepare", { avRecorderConfig });
+ if(m_mediaRecorder->hasError()){
+ qDebug() << "prepare falied:" << m_mediaRecorder->lastError();
+ p->set_value(QString());
+ }
+
+ QString ret;
+ Napi::Value value = m_mediaRecorder->call("getInputSurface");
+
+ if (value.IsPromise()) {
+ QJsPromise promise(value.As<Napi::Promise>());
+ promise.onThen([&](const Napi::CallbackInfo &info){
+ if (info.Length() < 1)
+ {
+ p->set_value(QString());
+ return;
+ }
+
+ ret = QString::fromStdString(info[0].As<Napi::String>());
+ p->set_value(ret);
+ });
+ }
+ else
+ p->set_value(QString());
+ });
+}
+
+void OhMediaRecorder::reset()
+{
+ QtOh::runOnJsUIThreadAndWait([this] {
+ if(m_mediaRecorder)
+ m_mediaRecorder->call("reset");
+ });
+}
+
+void OhMediaRecorder::pause()
+{
+ QtOh::runOnJsUIThreadAndWait([this] {
+ if(m_mediaRecorder)
+ m_mediaRecorder->call("pause");
+ });
+}
+
+void OhMediaRecorder::resume()
+{
+ QtOh::runOnJsUIThreadAndWait([this] {
+ if(m_mediaRecorder)
+ m_mediaRecorder->call("resume");
+ });
+}
+
+bool OhMediaRecorder::start()
+{
+ return QtOh::runOnJsUIThreadWithResult([this] () -> bool{
+ if(m_mediaRecorder)
+ return m_mediaRecorder->call("start").ToBoolean();
+
+ return false;
+ });
+}
+
+void OhMediaRecorder::stop()
+{
+ QtOh::runOnJsUIThreadAndWait([this] {
+ if(m_mediaRecorder)
+ m_mediaRecorder->call("stop");
+ });
+}
+
+void OhMediaRecorder::setAudioChannels(int numChannels)
+{
+ m_audioChannel = qBound(1, numChannels, 8);
+}
+
+void OhMediaRecorder::setAudioEncoder(AudioEncoder encoder)
+{
+ m_audioCodeC = encoder;
+}
+
+void OhMediaRecorder::setAudioEncodingBitRate(int bitRate)
+{
+ m_audioBitrate = bitRate;
+}
+
+void OhMediaRecorder::setAudioSamplingRate(int samplingRate)
+{
+ m_audioSampleRate = samplingRate;
+}
+
+void OhMediaRecorder::setVideoBitrate(int bitrate)
+{
+ m_videoBitrate = bitrate;
+}
+
+void OhMediaRecorder::setVideoEncoder(VideoEncoder encoder)
+{
+ m_videoCodec = encoder;
+}
+
+void OhMediaRecorder::setvideoFramSize(const QSize &size)
+{
+ m_videoFrameSize = size;
+}
+
+void OhMediaRecorder::setvideoFrameRate(int frameRate)
+{
+ m_videoFrameRate = frameRate;
+}
+
+void OhMediaRecorder::setOutputFile(const QString &path)
+{
+ m_outputFile = path;
+}
new file mode 100644
@@ -0,0 +1,88 @@
+#ifndef OHMEDIARECORDER_H
+#define OHMEDIARECORDER_H
+
+#include "qobject.h"
+#include <QSize>
+#include <QJsModule>
+
+QT_BEGIN_NAMESPACE
+
+class OhMediaRecorder :public QObject, public QJsModule
+{
+ Q_OBJECT
+public:
+ enum AudioEncoder {
+ AAC = 0,
+ VORBIS = 1,
+ FLAC = 2,
+ MP3 = 3,
+ G711MU = 4
+ };
+
+ enum VideoEncoder {
+ H263 = 0,
+ AVC = 1,
+ MPEG_2 = 2,
+ MPEG_4 = 3,
+ VP8 = 4
+ };
+
+ enum VideoSource {
+ YUV = 0,
+ ES = 1
+ };
+
+ enum OutputFormat {
+ CFT_MPEG_4 = 0,
+ CFT_MPEG_4A = 1,
+ };
+
+ OhMediaRecorder();
+ ~OhMediaRecorder();
+
+ void release();
+ QString prepare();
+ void reset();
+ void pause();
+ void resume();
+
+ bool start();
+ void stop();
+
+ void setAudioChannels(int numChannels);
+ void setAudioEncoder(AudioEncoder encoder);
+ void setAudioEncodingBitRate(int bitRate);
+ void setAudioSamplingRate(int samplingRate);
+
+ void setVideoBitrate(int bitrate);
+ void setVideoEncoder(VideoEncoder encoder);
+ void setvideoFramSize(const QSize &size);
+ void setvideoFrameRate(int frameRate);
+
+ void setOutputFile(const QString &path);
+
+Q_SIGNALS:
+ void error(int what, int extra);
+ void info(int what, int extra);
+
+private:
+ qptrdiff m_id;
+ QJsObject *m_mediaRecorder = nullptr;
+
+ //audio
+ int m_audioBitrate;
+ int m_audioChannel;
+ AudioEncoder m_audioCodeC;
+ int m_audioSampleRate;
+
+ //video
+ int m_videoBitrate;
+ VideoEncoder m_videoCodec;
+ QSize m_videoFrameSize;
+ int m_videoFrameRate;
+
+ QString m_outputFile;
+};
+
+QT_END_NAMESPACE
+#endif // OHMEDIARECORDER_H
new file mode 100644
@@ -0,0 +1,96 @@
+#include <qstring.h>
+#include "ohmultimediautils.h"
+
+QT_BEGIN_NAMESPACE
+
+int qt_findClosestValue(const QList<int> &list, int value)
+{
+ if (list.size() < 2)
+ return 0;
+
+ int begin = 0;
+ int end = list.size() - 1;
+ int pivot = begin + (end - begin) / 2;
+ int v = list.at(pivot);
+
+ while (end - begin > 1) {
+ if (value == v)
+ return pivot;
+
+ if (value > v)
+ begin = pivot;
+ else
+ end = pivot;
+
+ pivot = begin + (end - begin) / 2;
+ v = list.at(pivot);
+ }
+
+ return value - v >= list.at(pivot + 1) - value ? pivot + 1 : pivot;
+}
+
+bool qt_sizeLessThan(const QSize &s1, const QSize &s2)
+{
+ return s1.width() * s1.height() < s2.width() * s2.height();
+}
+
+class QtMultimediaUtils
+{
+ // QSharedPointer<QOpenHarmonyJsObject> m_jsUtils;
+
+public:
+ QtMultimediaUtils();
+ QString getDefaultMediaDirectory(OhMultimediaUtils::MediaType type);
+
+ bool requestPermission();
+};
+
+QtMultimediaUtils::QtMultimediaUtils()
+{
+ // m_jsUtils = qJsObjectLoader->create("JsMultimediaUtils");
+}
+
+QString QtMultimediaUtils::getDefaultMediaDirectory(OhMultimediaUtils::MediaType type)
+{
+ return QString();
+// QString dir = m_jsUtils->call<QString>("getMediaDirectory", type);
+// return std::move(dir);
+}
+
+bool QtMultimediaUtils::requestPermission()
+{
+ return true;
+ // return m_jsUtils->call<bool>("requestPermission", (int)OhMultimediaUtils::PerCamera);
+}
+
+Q_GLOBAL_STATIC(QtMultimediaUtils, gsUtils)
+int OhMultimediaUtils::getDeviceOrientation()
+{
+ //TODO
+ return 0;
+}
+
+void OhMultimediaUtils::enableOrientationListener(bool enable)
+{
+ //TODO
+ Q_UNUSED(enable);
+}
+
+void OhMultimediaUtils::registerMediaFile(const QString &file)
+{
+ //TODO
+ Q_UNUSED(file);
+}
+
+QString OhMultimediaUtils::getDefaultMediaDirectory(MediaType type)
+{
+ const QString &dir = gsUtils->getDefaultMediaDirectory(type);
+ return std::move(dir);
+}
+
+bool OhMultimediaUtils::qt_openharmonyRequestPermission()
+{
+ return gsUtils->requestPermission();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,43 @@
+#ifndef OHMULTIMEDIAUTILS_H
+#define OHMULTIMEDIAUTILS_H
+
+#include <qobject.h>
+#include <qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+int qt_findClosestValue(const QList<int> &list, int value);
+
+bool qt_sizeLessThan(const QSize &s1, const QSize &s2);
+
+inline uint qHash(const QSize& size, uint seed = 0) {
+ return qHash(size.width(), seed) ^ qHash(size.height(), seed << 1);
+}
+
+class OhMultimediaUtils
+{
+public:
+ enum MediaType {
+ Camera = 0,
+ Video = 1,
+ Image = 2,
+ Audio = 3,
+ Documents = 4,
+ Download = 5
+ };
+
+ enum Permissions{
+ PerCameraAndMicroPhone = 0,
+ PerCamera = 1,
+ PerMicroPhone = 2
+ };
+
+ static int getDeviceOrientation();
+ static void enableOrientationListener(bool enable);
+ static void registerMediaFile(const QString &file);
+ static QString getDefaultMediaDirectory(MediaType type);
+ static bool qt_openharmonyRequestPermission();
+};
+
+QT_END_NAMESPACE
+#endif // OHMULTIMEDIAUTILS_H
new file mode 100644
@@ -0,0 +1,3 @@
+{
+ "Keys": ["openharmony"]
+}
new file mode 100644
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopenharmonysgvideonode.h"
+
+#include <qsgmaterial.h>
+#include <qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOPenHarmonySGVideoNodeMaterialShader : public QSGMaterialShader
+{
+public:
+ void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ char const *const *attributeNames() const {
+ static const char *names[] = {
+ "qt_VertexPosition",
+ "qt_VertexTexCoord",
+ 0
+ };
+ return names;
+ }
+
+protected:
+
+ const char *vertexShader() const {
+ const char *shader =
+ "uniform highp mat4 qt_Matrix; \n"
+ "attribute highp vec4 qt_VertexPosition; \n"
+ "attribute highp vec2 qt_VertexTexCoord; \n"
+ "varying highp vec2 qt_TexCoord; \n"
+ "void main() { \n"
+ " qt_TexCoord = qt_VertexTexCoord; \n"
+ " gl_Position = qt_Matrix * qt_VertexPosition; \n"
+ "}";
+ return shader;
+ }
+
+ const char *fragmentShader() const {
+ static const char *shader =
+ "uniform sampler2D rgbTexture;"
+ "uniform lowp float opacity;"
+ ""
+ "varying highp vec2 qt_TexCoord;"
+ ""
+ "void main()"
+ "{"
+ " gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;"
+ "}";
+ return shader;
+ }
+
+ void initialize() {
+ m_id_matrix = program()->uniformLocation("qt_Matrix");
+ m_id_Texture = program()->uniformLocation("rgbTexture");
+ m_id_opacity = program()->uniformLocation("opacity");
+ }
+
+ int m_id_matrix;
+ int m_id_Texture;
+ int m_id_opacity;
+};
+
+class QOPenHarmonySGVideoNodeMaterial : public QSGMaterial
+{
+public:
+ QOPenHarmonySGVideoNodeMaterial()
+ : m_textureId(0)
+ , m_textureUpdated(false)
+ , m_opacity(1.0)
+ {
+ setFlag(Blending, false);
+ }
+
+ QSGMaterialType *type() const {
+ static QSGMaterialType theType;
+ return &theType;
+ }
+
+ QSGMaterialShader *createShader() const {
+ return new QOPenHarmonySGVideoNodeMaterialShader;
+ }
+
+ int compare(const QSGMaterial *other) const {
+ const QOPenHarmonySGVideoNodeMaterial *m = static_cast<const QOPenHarmonySGVideoNodeMaterial *>(other);
+ int diff = m_textureId - m->m_textureId;
+ if (diff)
+ return diff;
+
+ return (m_opacity > m->m_opacity) ? 1 : -1;
+ }
+
+ void updateBlending() {
+ setFlag(Blending, qFuzzyCompare(m_opacity, qreal(1.0)) ? false : true);
+ }
+
+ void updateTexture(GLuint id, const QSize &size) {
+ if (m_textureId != id || m_textureSize != size) {
+ m_textureId = id;
+ m_textureSize = size;
+ m_textureUpdated = true;
+ }
+ }
+
+ void bind()
+ {
+ glBindTexture(GL_TEXTURE_2D, m_textureId);
+ if (m_textureUpdated) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ m_textureUpdated = false;
+ }
+ }
+
+ QSize m_textureSize;
+ GLuint m_textureId;
+ bool m_textureUpdated;
+ qreal m_opacity;
+};
+
+
+QOPenHarmonySGVideoNode::QOPenHarmonySGVideoNode(const QVideoSurfaceFormat &format)
+ : m_format(format)
+{
+ setFlags(OwnsMaterial | UsePreprocess);
+ m_material = new QOPenHarmonySGVideoNodeMaterial;
+ setMaterial(m_material);
+}
+
+QOPenHarmonySGVideoNode::~QOPenHarmonySGVideoNode()
+{
+ m_frame = QVideoFrame();
+}
+
+void QOPenHarmonySGVideoNode::setCurrentFrame(const QVideoFrame &frame, FrameFlags)
+{
+ QMutexLocker lock(&m_frameMutex);
+ m_frame = frame;
+ markDirty(DirtyMaterial);
+}
+
+void QOPenHarmonySGVideoNodeMaterialShader::updateState(const RenderState &state,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(oldMaterial);
+ QOPenHarmonySGVideoNodeMaterial *mat = static_cast<QOPenHarmonySGVideoNodeMaterial *>(newMaterial);
+ program()->setUniformValue(m_id_Texture, 0);
+
+ mat->bind();
+
+ if (state.isOpacityDirty()) {
+ mat->m_opacity = state.opacity();
+ mat->updateBlending();
+ program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity));
+ }
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+}
+
+void QOPenHarmonySGVideoNode::preprocess()
+{
+ QMutexLocker lock(&m_frameMutex);
+
+ GLuint texId = 0;
+ if (m_frame.isValid())
+ texId = m_frame.handle().toUInt();
+
+ m_material->updateTexture(texId, m_frame.size());
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPENHARMONYSGVIDEONODE_H
+#define QOPENHARMONYSGVIDEONODE_H
+
+#include <private/qsgvideonode_p.h>
+#include <qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOPenHarmonySGVideoNodeMaterial;
+
+class QOPenHarmonySGVideoNode : public QSGVideoNode
+{
+public:
+ QOPenHarmonySGVideoNode(const QVideoSurfaceFormat &format);
+ ~QOPenHarmonySGVideoNode();
+
+ void setCurrentFrame(const QVideoFrame &frame, FrameFlags flags);
+ QVideoFrame::PixelFormat pixelFormat() const { return m_format.pixelFormat(); }
+ QAbstractVideoBuffer::HandleType handleType() const { return QAbstractVideoBuffer::GLTextureHandle; }
+
+ void preprocess();
+
+private:
+ QOPenHarmonySGVideoNodeMaterial *m_material;
+ QMutex m_frameMutex;
+ QVideoFrame m_frame;
+ QVideoSurfaceFormat m_format;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONYSGVIDEONODE_H
new file mode 100644
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopenharmonysgvideonodeplugin.h"
+#include "qopenharmonysgvideonode.h"
+
+QT_BEGIN_NAMESPACE
+
+QList<QVideoFrame::PixelFormat> QOPenHarmonySGVideoNodeFactoryPlugin::supportedPixelFormats(
+ QAbstractVideoBuffer::HandleType handleType) const
+{
+ QList<QVideoFrame::PixelFormat> pixelFormats;
+
+ if (handleType == QAbstractVideoBuffer::GLTextureHandle)
+ pixelFormats.append(QVideoFrame::Format_BGR32);
+
+ return pixelFormats;
+}
+
+QSGVideoNode *QOPenHarmonySGVideoNodeFactoryPlugin::createNode(const QVideoSurfaceFormat &format)
+{
+ if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat()))
+ return new QOPenHarmonySGVideoNode(format);
+
+ return 0;
+}
+
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPENHARMONYSGVIDEONODEPLUGIN_H
+#define QOPENHARMONYSGVIDEONODEPLUGIN_H
+
+#include <private/qsgvideonode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOPenHarmonySGVideoNodeFactoryPlugin : public QSGVideoNodeFactoryPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QSGVideoNodeFactoryInterface_iid
+ FILE "openharmony_videonode.json")
+
+public:
+ QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
+ QSGVideoNode *createNode(const QVideoSurfaceFormat &format);
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONYSGVIDEONODEPLUGIN_H
new file mode 100644
@@ -0,0 +1,18 @@
+TARGET = qtsgvideonode_openharmony
+
+QT += quick multimedia-private qtmultimediaquicktools-private
+
+HEADERS += \
+ qopenharmonysgvideonode.h \
+ qopenharmonysgvideonodeplugin.h
+
+SOURCES += \
+ qopenharmonysgvideonode.cpp \
+ qopenharmonysgvideonodeplugin.cpp
+
+OTHER_FILES += openharmony_videonode.json
+
+PLUGIN_TYPE = video/videonode
+PLUGIN_EXTENDS = quick
+PLUGIN_CLASS_NAME = QOPenharmonySGVideoNodeFactoryPlugin
+load(qt_plugin)
@@ -1,13 +1,11 @@
TARGET = qtaudio_opensles
QT += multimedia-private core-private
-LIBS += -lOpenSLES
-
HEADERS += \
qopenslesplugin.h \
qopenslesengine.h \
qopenslesdeviceinfo.h \
- qopenslesaudioinput.h \
+ qopenslesaudioinput.h\
qopenslesaudiooutput.h
SOURCES += \
@@ -17,6 +15,8 @@ SOURCES += \
qopenslesaudioinput.cpp \
qopenslesaudiooutput.cpp
+LIBS += -lOpenSLES
+
OTHER_FILES += \
opensles.json
@@ -84,13 +84,17 @@ static bool hasRecordingPermission()
}
static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context)
+{
+ // Process buffer in main thread
+ QMetaObject::invokeMethod(reinterpret_cast<QOpenSLESAudioInput*>(context), "processBuffer");
+}
#else
static void bufferQueueCallback(SLBufferQueueItf, void *context)
-#endif
{
// Process buffer in main thread
QMetaObject::invokeMethod(reinterpret_cast<QOpenSLESAudioInput*>(context), "processBuffer");
}
+#endif
QOpenSLESAudioInput::QOpenSLESAudioInput(const QByteArray &device)
: m_device(device)
@@ -209,8 +213,10 @@ QIODevice *QOpenSLESAudioInput::start()
bool QOpenSLESAudioInput::startRecording()
{
+#ifdef ANDROID
if (!hasRecordingPermission())
return false;
+#endif
m_processedBytes = 0;
m_clockStamp.restart();
@@ -360,8 +360,11 @@ void QOpenSLESAudioOutput::onEOSEvent()
{
if (m_state != QAudio::ActiveState)
return;
-
+#if defined(Q_OS_OPENHARMONY)
+ SLOHBufferQueueState state;
+#else
SLBufferQueueState state;
+#endif
if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state))
return;
@@ -426,7 +429,15 @@ void QOpenSLESAudioOutput::playCallback(SLPlayItf player, void *ctx, SLuint32 ev
Q_EMIT audioOutput->notify();
}
-
+#if defined(Q_OS_OPENHARMONY)
+void QOpenSLESAudioOutput::bufferQueueCallback (SLOHBufferQueueItf bufferQueue, void *ctx, SLuint32 size)
+{
+ SLOHBufferQueueState state;
+ (*bufferQueue)->GetState(bufferQueue, &state);
+ QOpenSLESAudioOutput *audioOutput = reinterpret_cast<QOpenSLESAudioOutput *>(ctx);
+ audioOutput->bufferAvailable(state.count, state.index);
+}
+#else
void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx)
{
SLBufferQueueState state;
@@ -434,7 +445,7 @@ void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, voi
QOpenSLESAudioOutput *audioOutput = reinterpret_cast<QOpenSLESAudioOutput *>(ctx);
audioOutput->bufferAvailable(state.count, state.playIndex);
}
-
+#endif
bool QOpenSLESAudioOutput::preparePlayer()
{
if (m_startRequiresInit)
@@ -476,7 +487,11 @@ bool QOpenSLESAudioOutput::preparePlayer()
#ifndef ANDROID
const int iids = 2;
+#if defined(Q_OS_OPENHARMONY)
+ const SLInterfaceID ids[iids] = { SL_IID_OH_BUFFERQUEUE, SL_IID_VOLUME };
+#else
const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME };
+#endif
const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
#else
const int iids = 3;
@@ -490,10 +505,18 @@ bool QOpenSLESAudioOutput::preparePlayer()
if (SL_RESULT_SUCCESS != (*engine)->CreateAudioPlayer(engine,
&m_playerObject,
&audioSrc,
+#if defined(Q_OS_OPENHARMONY)
+ nullptr,
+ 0,
+ nullptr,
+ nullptr
+#else
&audioSink,
iids,
ids,
- req)) {
+ req
+#endif
+ )) {
qWarning() << "Unable to create AudioPlayer";
setError(QAudio::OpenError);
return false;
@@ -520,7 +543,11 @@ bool QOpenSLESAudioOutput::preparePlayer()
// Buffer interface
if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject,
+#if defined(Q_OS_OPENHARMONY)
+ SL_IID_OH_BUFFERQUEUE,
+#else
SL_IID_BUFFERQUEUE,
+#endif
&m_bufferQueueItf)) {
setError(QAudio::FatalError);
return false;
@@ -540,7 +567,8 @@ bool QOpenSLESAudioOutput::preparePlayer()
setError(QAudio::FatalError);
return false;
}
-
+#ifndef Q_OS_OPENHARMONY
+ /* TODO for openharmony*/
if (SL_RESULT_SUCCESS != (*m_playItf)->RegisterCallback(m_playItf, playCallback, this)) {
setError(QAudio::FatalError);
return false;
@@ -555,7 +583,7 @@ bool QOpenSLESAudioOutput::preparePlayer()
setError(QAudio::FatalError);
return false;
}
-
+#endif
// Volume interface
if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject,
SL_IID_VOLUME,
@@ -47,6 +47,11 @@
#include <QTime>
#include <QIODevice>
+#if defined(Q_OS_OPENHARMONY)
+#include <SLES/OpenSLES_OpenHarmony.h>
+#include <SLES/OpenSLES_Platform.h>
+#endif
+
QT_BEGIN_NAMESPACE
class QOpenSLESAudioOutput : public QAbstractAudioOutput
@@ -90,8 +95,11 @@ private:
void bufferAvailable(quint32 count, quint32 playIndex);
static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event);
+#if defined(Q_OS_OPENHARMONY)
+ static void bufferQueueCallback(SLOHBufferQueueItf bufferQueue, void *ctx, SLuint32 size);
+#else
static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx);
-
+#endif
bool preparePlayer();
void destroyPlayer();
void stopPlayer();
@@ -110,7 +118,11 @@ private:
SLObjectItf m_playerObject;
SLPlayItf m_playItf;
SLVolumeItf m_volumeItf;
+#if defined(Q_OS_OPENHARMONY)
+ SLOHBufferQueueItf m_bufferQueueItf;
+#else
SLBufferQueueItf m_bufferQueueItf;
+#endif
QIODevice *m_audioSource;
char *m_buffers;
qreal m_volume;
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qopenslesengine.h"
-
#include "qopenslesaudioinput.h"
#include <qdebug.h>
@@ -122,6 +121,7 @@ QList<QByteArray> QOpenSLESEngine::availableDevices(QAudio::Mode mode) const
} else {
devices << "default";
}
+
return devices;
}
@@ -1,4 +1,4 @@
-/****************************************************************************
+/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
@@ -42,6 +42,7 @@
#include "qopenslesengine.h"
#include "qopenslesdeviceinfo.h"
#include "qopenslesaudioinput.h"
+
#include "qopenslesaudiooutput.h"
QT_BEGIN_NAMESPACE
@@ -17,6 +17,10 @@ android {
SUBDIRS += android opensles
}
+openharmony {
+ SUBDIRS += openharmony ohaudiodevice
+}
+
qnx {
qtConfig(mmrenderer): SUBDIRS += qnx
SUBDIRS += audiocapture
@@ -41,7 +41,11 @@
#include <QtMultimedia/qvideosurfaceformat.h>
+#ifdef Q_OS_OPENHARMONY
+#include <EGL/eglext.h>
+#else
#include <GLES2/gl2ext.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -1,6 +1,7 @@
TEMPLATE = subdirs
SUBDIRS += multimedia
+#SUBDIRS += openharmony
include($$OUT_PWD/multimedia/qtmultimedia-config.pri)
QT_FOR_CONFIG += multimedia-private