Storing the Content of MediaPlayer (QML/Qt) - android

I'm developing a phone application using Qt. I have used Qt C++ classes for my whole app but at some point I had to use Qt QML Types for phone camera function in my application and now I am able to display camera's content on a MediaPlayer. I was wondering if it is possible to aslo save this content on phone storage as a .mp4 file or any other formats?
Here is a piece of the code where I display this content on MediaPlayer:
import QtQuick 2.0
import QtMultimedia 5.0
Item {
id: videoPreview
property alias source : player.source
signal closed
MediaPlayer {
id: player
autoPlay: true
onStatusChanged: {
if (status == MediaPlayer.EndOfMedia)
videoPreview.closed();
}
}
VideoOutput {
source: player
anchors.fill : parent
}
MouseArea {
anchors.fill: parent
onClicked: {
videoPreview.closed();
}
}
}
I would really appreciate any kind of hints or helps since I'm a beginner in QML Types :).
And hereby is my main QML file.
import QtQuick 2.0
import QtMultimedia 5.4
Rectangle {
id : cameraUI
width: 640
height: 480
color: "black"
state: "VideoCapture"
states: [
State {
name: "VideoCapture"
StateChangeScript {
script: {
camera.position = Camera.FrontFace
camera.captureMode = Camera.CaptureVideo
camera.start()
}
}
},
State {
name: "VideoPreview"
StateChangeScript {
script: {
camera.stop()
}
}
}
]
Camera {
id: camera
captureMode: Camera.CaptureVideo
videoRecorder {
resolution: "640x480"
frameRate: 30
}
}
VideoPreview {
id : videoPreview
anchors.fill : parent
onClosed: cameraUI.state = "VideoCapture"
visible: cameraUI.state == "VideoPreview"
focus: visible
//don't load recorded video if preview is invisible
source: visible ? camera.videoRecorder.actualLocation : ""
}
VideoOutput {
id: viewfinder
visible: cameraUI.state == "VideoCapture"
x: 0
y: 0
// width: parent.width
width: parent.width - stillControls.buttonsPanelWidth
height: parent.height
source: camera
autoOrientation: true
}
VideoCaptureControls {
id: videoControls
anchors.fill: parent
camera: camera
visible: cameraUI.state == "VideoCapture"
onPreviewSelected: cameraUI.state = "VideoPreview"
}
}

Something like this should work. For more properties check CameraRecorder documentation.
Camera
{
id: camera
captureMode: Camera.CaptureVideo
videoRecorder.muted: true // set any property of recording.
}
VideoOutput
{
source: camera
width: parent.width*0.8
height: parent.height*0.8
autoOrientation: true
anchors.centerIn: parent
MouseArea
{
property var myInt:0
anchors.fill: parent
onClicked:
{
camera.searchAndLock()
camera.videoRecorder.record()
if( myInt > 1 )
{
camera.videoRecorder.stop()
console.log(camera.videoRecorder.outputLocation)
}
myInt=myInt+1
}
}
}

Related

qml CaptureSession freezes app on Android

I have a bug when using CaptureSession on Qt6.4, if I use it in a dynamic component, when I destroy it, the app freezes and I have to kill it.
This problem does not exist when I compile for Desktop.
I tried on Android 11 & 13, the result is the same.
Also it was perfectly working with Qt5 when only using VideoOuput & Camera.
I wrote a minimal example using a Loader, that creates the freeze. If someone has an idea to fix it :
import QtQuick 6.4
import QtQuick.Window 6.4
import QtQuick.Controls 6.4
import QtMultimedia
ApplicationWindow
{
id: window
visible: true
width: Screen.desktopAvailableWidth
height: Screen.desktopAvailableHeight
Loader {
id: loader
anchors.fill: parent
active: activeBtn.checked
sourceComponent: Component {
Item {
id: item
Camera {
id: camera
active: true
focusMode: Camera.FocusModeAutoNear
}
CaptureSession {
camera: camera
videoOutput: videoOutput
}
VideoOutput {
id: videoOutput
anchors.fill: parent
fillMode: VideoOutput.Stretch
}
}
}
}
Button {
id : activeBtn
text: "Active CAM"
checkable: true
anchors {
right: parent.right
bottom: parent.bottom
}
}
}
The app freezes as soon as I unload the component, so when CaptureSession is destroyed.
I would make sure the Camera is stopped before destroying the Component.
In other words, we need to acknowledge on some backends, the camera may be running in a different thread, and, we must make sure we give it sufficient time to terminate the camera and the corresponding VideoOutput preview before forcibly destroying the QML components.
We can keep your original property binding for starting the camera, i.e.
property bool cameraActive: false
Loader {
id: loader
anchors.fill: parent
active: activeBtn.checked || cameraActive
So that will create the component as soon as the Button is checked, HOWEVER, during the initialization of the Component, we changed cameraActive = true; so that it prevents the unchecking of the same Button to destroy the component.
Component.onCompleted: {
cameraActive = true;
}
We use that Button event to stop the Camera with camera.stop().
Connections {
target: activeBtn
function onCheckedChanged() {
if (!activeBtn.checked && camera.active) {
camera.stop();
}
}
}
As soon as we know the Camera has stopped with camera.active === false, then, we change cameraActive = false and that should resume the destruction of your Component.
Camera {
id: camera
active: true
focusMode: Camera.FocusModeAutoNear
onActiveChanged: cameraActive = active
}
Here's your complete code with all of the above changes. Note that I haven't been able to fully verify the code below, so, please treat the code as an educational guide only:
import QtQuick 6.4
import QtQuick.Window 6.4
import QtQuick.Controls 6.4
import QtMultimedia
ApplicationWindow
{
id: window
visible: true
width: Screen.desktopAvailableWidth
height: Screen.desktopAvailableHeight
property bool cameraActive: false
Loader {
id: loader
anchors.fill: parent
active: activeBtn.checked || cameraActive
sourceComponent: Component {
Item {
id: item
Camera {
id: camera
active: true
focusMode: Camera.FocusModeAutoNear
onActiveChanged: cameraActive = active
}
CaptureSession {
camera: camera
videoOutput: videoOutput
}
VideoOutput {
id: videoOutput
anchors.fill: parent
fillMode: VideoOutput.Stretch
}
Connections {
target: activeBtn
function onCheckedChanged() {
if (!activeBtn.checked && camera.active) {
camera.stop();
}
}
}
Component.onCompleted: {
cameraActive = true;
}
}
}
}
Button {
id : activeBtn
text: "Active CAM"
checkable: true
anchors {
right: parent.right
bottom: parent.bottom
}
}
}

How to send mouse clicked coordinates from Flickable to ListView?

I'm developing an application for Android.
I have some ChartViews placed in the ListView.
Above ListView I have got a Flickable to scroll ChartViews synchronously in the x axis.
I need to show the coordinates of the point on the chart to which the user tap.
But I can't because flickable catches mouse click events and does not propagate it to other objects below.
I need the mouse event to come to a specific delegate that the user tapped on.
Is it possible to solve this problem?
Is there a way to get mouse coordinates during flick?
Is there a way to propagate mouse events to the ListView as if they came from the user?
Rectangle {
color: "#ffffff"
Component {
id: chartDelegate
Rectangle {
id: rootDelegRect
anchors {left: parent.left; right: parent.right }
height: 350
ChartView {
anchors { fill: parent;}
x: -10
y: -10
width: parent.width + 20
height: parent.height + 20
legend.visible: false
LineSeries {
name: "LineSeries"
axisX: ValueAxis {
min: 0
max: 4
labelFormat: "%.0f"
}
axisY: ValueAxis {
min: 0
max: 6
labelFormat: "%.0f"
}
XYPoint { x: 0; y: 0.0 }
XYPoint { x: 1; y: 5.2 }
XYPoint { x: 2; y: 2.4 }
XYPoint { x: 3; y: 0.1 }
XYPoint { x: 4; y: 5.1 }
}
}
MouseArea {
anchors.fill: parent
hoverEnabled :true
onMouseXChanged: console.log(mouseX,mouseY);//does not work when clicked
}
}
}
ListView { id: listViewCharts; objectName: "chart";
clip: true
anchors.top: parent.top; anchors.bottom: parent.bottom;
width: 1500
contentX:baseFlick.contentX; contentY:baseFlick.contentY;
model: listViewIdsModel
cacheBuffer: 1500
delegate: chartDelegate
}
Flickable {
id: baseFlick
anchors.fill: parent
contentHeight: listViewCharts.contentHeight
contentWidth: listViewCharts.width
}
}
Apparently this problem can only be solved using c++
To capture Flickable mouse events, do the following:
1) add the objectName of the object is Flickable
objectName: "baseFlick"
2) in c++ after creating a qml scene. Find the baseFlick object and Installs an event filter on this object
auto rootObj = engine.rootObjects().at(0);
auto item = rootObj->findChild<QQuickItem *>("baseFlick");
if(item!=nullptr){
item->installEventFilter(this);
} else qDebug()<<"ERR baseFlick item=nullptr";
3) implement the eventFilter virtual function
bool IDS2forUser::eventFilter(QObject *watched, QEvent *event)
{
...
return false;
}
In order to propagate mouse events to the ListView as if they came from the user.
In the eventFilter function:
1) Find the ListView (chart) object
auto rootObj = engine.rootObjects().at(0);
auto item = rootObj->findChild<QQuickItem *>("chart");
2) Find all of his children.
You need to look for children every time as their number may change.
Note, you should use the QQuickItem::childItems () function instead of QObject::children()
void IDS2forUser::getAllObjects(QQuickItem *parent, QList<QQuickItem *> &list) {
QList<QQuickItem *> children = parent->childItems();
foreach (QQuickItem *item, children) {
list.append(item);
getAllObjects(item,list);
}
}
3) Convert all coordinates to local coordinates of the object. And send all children events.
The whole function is presented below.
bool IDS2forUser::eventFilter(QObject *watched, QEvent *event)
{
auto rootObj = engine.rootObjects().at(0);
auto item = rootObj->findChild<QQuickItem *>("chart");
QList<QQuickItem *> list;
getAllObjects(item, list);
QQuickItem *watchedIt = dynamic_cast<QQuickItem *>(watched);
if(!watchedIt)return false;
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(event);
if(!mouseEvent)return false;
foreach (QQuickItem *item, list) {
QPointF point=item->mapFromItem(watchedIt,mouseEvent->localPos());
if(point.x()<0 || point.y()<0 || point.x()>=item->width() || point.y()>=item->height())continue;
QMouseEvent mouseEvent2(mouseEvent->type(),
point,
mouseEvent->button(),mouseEvent->buttons(),Qt::NoModifier);
QCoreApplication::sendEvent(item, &mouseEvent2);
}
return false;
}

How to build dynamically loading Qml pages onto main and swipe between pages?

I have a 3 Qml page like home.qml, weather.qml and currency.qml. In home.qml page I need to load (as I thought the right way) 3 page and I can use Qml Swipe with Loader to load pages. I need to swipe between pages like 1->2->3 and 3->2->1.
Also, I need show that home.qml page is the first page. I check the **doc.qt.io/qt-5/qml-qtquick-controls2-swipeview . html link but I couldn’t load my other 2 pages, so I can swipe. Where I can get the info, so I can build dynamically loading Qml pages onto main and swipe between pages.
Any help please?
UPDATED WORKING CODE:
Tested on Qt5.9.2 in QML app project.
Code is show in a simple way. Here is the code:
I create a new VPlay APP. It was only In Main.qml file. Than I add 3 page (Page1.qml, Page2.qml and Page3.qml)
In Main.qml I add below code.
import VPlayApps 1.0
import QtQuick 2.4
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2
Page {
anchors.fill: parent
// Background
Rectangle {
y: 0; width: parent.width; height: parent.height
gradient: Gradient {
GradientStop { position: 0.00; color: “#1AD6FD” }
GradientStop { position: 1.00; color: “#1D62F0” }
}
}
SwipeView {
id: view
currentIndex: 0
anchors.fill: parent
anchors.top: parent.top
Item{
id: firstItem
Loader {
// index 0
id: pageOneLoader
source: “Page1.qml”
anchors.fill: parent
anchors.top: parent.top
}
}
Item{
id: secondItem
Loader {
// index 1
id: pageSecondLoader
source: “Page2.qml”
anchors.fill: parent
anchors.top: parent.top
}
}
Item{
id: thirdItem
Loader {
// index 2
id: pageThirdLoader
source: “Page3.qml”
anchors.fill: parent
anchors.top: parent.top
}
}
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
And Each page (page1, page2 etc) I add below code;
// emitting a Signal could be another option
Component.onDestruction: {
cleanup()
}
Code is show in a simple way. Here is the code:
I create a new VPlay APP. It was only In Main.qml file. Than I add 3 page (Page1.qml, Page2.qml and Page3.qml) In Main.qml I add below code.
import VPlayApps 1.0
import QtQuick 2.4
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2
Page {
anchors.fill: parent
// Background
Rectangle {
y: 0; width: parent.width; height: parent.height
gradient: Gradient {
GradientStop { position: 0.00; color: “#1AD6FD” }
GradientStop { position: 1.00; color: “#1D62F0” }
}
}
SwipeView {
id: view
currentIndex: 0
anchors.fill: parent
anchors.top: parent.top
Item{
id: firstItem
Loader {
// index 0
id: pageOneLoader
source: “Page1.qml”
anchors.fill: parent
anchors.top: parent.top
}
}
Item{
id: secondItem
Loader {
// index 1
id: pageSecondLoader
source: “Page2.qml”
anchors.fill: parent
anchors.top: parent.top
}
}
Item{
id: thirdItem
Loader {
// index 2
id: pageThirdLoader
source: “Page3.qml”
anchors.fill: parent
anchors.top: parent.top
}
}
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
And Each page (page1, page2 etc) I add below code;
// emitting a Signal could be another option
Component.onDestruction: {
cleanup()
}

Qt/QML SwipeDelegate doesn't work properly on mobile devices (Android, iOS)

I'm new to Qt/QML programming and am trying to get the following example to run properly on a mobile device. When I try to "swipe right" and then tap the remove button, the "Listview-item" will not be deleted. On Desktop all works fine, but on a mobile device it doesn't work properly. Can anyone help me with my problem?
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: appWindow
visible: true
ListView {
id: listView
anchors.fill: parent
model: ListModel {
ListElement { name: "Swipe Delegate - Test 1" }
ListElement { name: "Swipe Delegate - Test 2" }
ListElement { name: "Swipe Delegate - Test 3" }
ListElement { name: "Swipe Delegate - Test 4" }
}
delegate: SwipeDelegate {
id: swipeDelegate
text: model.name
width: parent.width
ListView.onRemove: SequentialAnimation {
PropertyAction {
target: swipeDelegate
property: "ListView.delayRemove"
value: true
}
NumberAnimation {
target: swipeDelegate
property: "height"
to: 0
easing.type: Easing.InOutQuad
}
PropertyAction {
target: swipeDelegate;
property: "ListView.delayRemove";
value: false
}
}
swipe.right: Label {
id: deleteLabel
text: qsTr("Delete")
color: "white"
verticalAlignment: Label.AlignVCenter
padding: 12
height: parent.height
anchors.right: parent.right
SwipeDelegate.onClicked: listView.model.remove(index)
background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
}
}
}
}
}
You can add a MouseArea with an onClicked-event inside the Rectangle. Here is the example:
background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
MouseArea {
anchors.fill: parent
onClicked: listView.model.remove(index)
}
}

Cant get button to appear

I'm trying to make some sort of fotboll app for android. The problem i'm facing right now, is that I need to make a button in the corner of the screen, (4 different) which you move the game character with. But I've made 1 button now, for moving up, but it doesnt appear on the screen.
Main file:
import QtQuick 2.0
import QtQuick.Window 2.1
Item {
id:root
width:Screen.width
height:Screen.height-10
focus:true
Keys.onPressed: {
if(event.key===Qt.Key_Up)
{
event.accepted = true;
player.y=(player.y) - 40
}
if(event.Key === Qt.Key_Down){
event.accepted = true;
player.y = (player.y)+ 40
}
if (event.key === Qt.Key_Right)
{ event.accepted=true;
player.x=(player.x)+40
}
if (event.key === Qt.Key_Left)
{event.accepted = true;
player.x=(player.x) -40
}
}
Flickable {
width:Screen.width
height:Screen.height
contentHeight: Screen.height*2
contentWidth:Screen.width
interactive:true
boundsBehavior: Flickable.StopAtBounds
contentY: Math.min(contentHeight-height, Math.max(0, player.y-height/2))
contentX: Math.min(contentWidth-width, Math.max(0, player.x-width/2))
Image{
id: feild
anchors.fill:parent
source:"Namnlös.png"
sourceSize.height:Screen.height*2
sourceSize.width:Screen.width
}
Image {
id: player
source:"asd.png"
x:Screen.width/2
y:Screen.height/2
}
}
}
Button file:
import QtQuick 2.0
import QtQuick.Window 2.1
Rectangle {
id: simplebutton
color: "grey"
width: 100; height: 50
Text {
id: buttonLabel
anchors.centerIn: parent
text: "Up"
}
MouseArea {
id: buttonMouseArea
anchors.fill: parent
onClicked: console.log(buttonLabel.text + "clicked" )
}
}
Rectangle {
id: button
property color buttonColor: "lightblue"
property color onHoverColor: "gold"
property color borderColor: "white"
signal buttonClick()
onButtonClick: {
console.log(buttonLabel.text + " clicked" )
}
MouseArea {
onClicked: buttonClick()
hoverEnabled: true
onEntered: parent.border.color = onHoverColor
onExited: parent.border.color = borderColor
}
color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
}
Two things:
1) You need to add your button to your layout! Since there you don't add your button into your main file, it won't appear.
2) You need to split your Button file into two separate qml file, since you can only have one "root" item per file.

Categories

Resources