您现在的位置是:网站首页> C/C++
QT实测经验教训
- C/C++
- 2022-04-07
- 1366人已阅读
Qt播放视频报错 DirectShowPlayerService::doRender: Unresolved error code 0x80040266
QT调用自己的Activity得到返回值
activity在AndroidManifest.xml中定义
<activity android:name=".XNActivity">
<intent-filter>
<action android:name="org.qtproject.example.HelloWindow.XNActivity"/> <!--一定要指定通过名称调用-->
<category android:name="android.intent.category.DEFAULT"/><!--一定要指定默认值-->
</intent-filter>
</activity>
XNActivity布局代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ff0000ff"
>
<Button
android:layout_width="match_parent"
android:text="@string/click"
android:layout_height="wrap_content"
android:id="@+id/mybt"
/>
</LinearLayout>
XNActivity.Java代码
package org.qtproject.example.HelloWindow;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import org.qtproject.qt5.android.bindings.QtActivity;
public class XNActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_xn);
findViewById(R.id.mybt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent =new Intent(XNActivity.this, QtActivity.class);
intent.putExtra("select","123中华民族");
setResult(Activity.RESULT_OK,intent);
finish();//结束当前Activity,返回上一个activity
}
});
}
}
QT端调用代码
ActivityResult *m_ActivityResult;
QAndroidJniEnvironment env;
if(m_ActivityResult!=NULL)
{
delete m_ActivityResult;
}
m_ActivityResult = new ActivityResult(0);
connect(m_ActivityResult,SIGNAL(sendData(QString)),this,SLOT(sendData(QString)));
QtAndroid::runOnAndroidThread([=](){
QAndroidJniObject action = QAndroidJniObject::fromString("org.qtproject.example.HelloWindow.XNActivity");
QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String;)V", action.object<jstring>());
QtAndroid::startActivity(intent, 0, m_ActivityResult);
});
ActivityResult .h
#ifndef ACTIVITYRESULT_H
#define ACTIVITYRESULT_H
#include <QAndroidActivityResultReceiver>
class ActivityResult : public QObject,public QAndroidActivityResultReceiver
{
Q_OBJECT
signals:
void sendData(QString);
public:
int requestId;
ActivityResult(int id, QObject *parent = nullptr) : QObject(parent), requestId(id) {}
void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data);
};
#endif // ACTIVITYRESULT_H
ActivityResult .cpp
#include <QDebug>
void ActivityResult::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
{
/*
QString myStr=QString("receiverRequestCode %1 resultCode %2").arg(receiverRequestCode).arg(resultCode);
emit sendData(myStr);
return;
*/
if(receiverRequestCode == requestId)
{
if(resultCode ==-1)// RESULT_OK)
{
QAndroidJniObject name=QAndroidJniObject::fromString("select");
QAndroidJniObject str = data.callObjectMethod("getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;",name.object<jstring>());
if(str==NULL)
{
emit sendData(tr("返回为空"));
return;
}
str.toString();
emit sendData(str.toString());
return;
}
else
{
//some code here
}
}
}
QT调用JAVA代码及数据类型对照
数据类型签名对照表:
jobject
Ljava/lang/Object;
jclass
Ljava/lang/Class;
jstring
Ljava/lang/String;
jthrowable
Ljava/lang/Throwable;
jobjectArray
[Ljava/lang/Object;
jarray
[<type>
jbooleanArray
[Z
jbyteArray
[B
jcharArray
[C
jshortArray
[S
jintArray
[I
jlongArray
[J
jfloatArray
[F
jdoubleArray
[D
Primitive Types
jboolean
Z
jbyte
B
jchar
C
jshort
S
jint
I
jlong
J
jfloat
F
jdouble
D
void
V
Custom type
L<fully-qualified-name>;
函数描述如:
(输入参数1;输入参数2;)返回类型;是传的要加;
(I)V
(Ljava/lang/String;)Ljava/lang/Object;
(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;
MyJavaClass.java
package com;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import android.app.Activity;
import android.content.ContentValues;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.LinearLayout;
public class MyJavaClass
{
public static int mystatic(int n) //静态函数;
{
return n+1;
}
public int getNum() //没有参数;
{
return 12345678;
}
public int setNum( int n ) //有参调用
{
return n;
}
public String getStr(String perfix) //返回对象
{
String teststr = new String("hello " + perfix);
return teststr;
}
boolean boolTest() //返回bool类型
{
return true;
}
}
QT调用
int n=5;
jint A=QAndroidJniObject::callStaticMethod<jint>
("com/MyJavaClass" // class name
, "mystatic" // method name
, "(I)I" // signature
, n);
QString s = QString::number(A);
QMessageBox::information(NULL, tr("提示"), s,QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
QAndroidJniObject QAndroidJniObject_MyJavaClass_obj("com/MyJavaClass");
jint B = QAndroidJniObject_MyJavaClass_obj.callMethod<jint>("getNum");
QString ss = QString::number(B);
QMessageBox::information(NULL, tr("提示"), ss,QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
return;
Java调用QT
** Java QtActivity 代码**
package org.qtproject.qt5.android.bindings;
public class QtActivity extends Activity
{
public static native int sendVideoData(long unused, byte[] data, int len, long timestamp);
public static native int sendCh340xData(byte[] data, int len);
}
c++ 导出函数
qt 会生成 so 文件 直接导出 只要导出函数签名一致 java 就能 调用 不需要调用 System.loadLibrary("")
#if defined(Q_OS_ANDROID)
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL
Java_org_qtproject_qt5_android_bindings_QtActivity_sendCh340xData(
JNIEnv *env, jclass type,
jbyteArray data_, jint len)
{
jbyte *data = env->GetByteArrayElements(data_, NULL);
QByteArray buff;
buff.resize(len);
memcpy(buff.data(), (uint8_t *)data, len);
QMetaObject::invokeMethod(g_cH34xWidget, "onData", Qt::QueuedConnection, Q_ARG(QByteArray, buff));
env->ReleaseByteArrayElements(data_, data, 0);
return 0;
}
#ifdef __cplusplus
}
#endif
#if defined(Q_OS_ANDROID)
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL
Java_org_qtproject_qt5_android_bindings_QtActivity_sendVideoData(
JNIEnv *env, jclass type, jlong cptr,
jbyteArray data_, jint len, jlong timestamp)
{
jbyte *data = env->GetByteArrayElements(data_, NULL);
uint8_t *buf = (uint8_t *)data;
TcpClient::instance()->sendVideo((uint8_t *)buf, len);
env->ReleaseByteArrayElements(data_, data, 0);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif
JAVA代码
package com.qdian.testmyapp;
public class MainActivity extends AppCompatActivity {
{
public native String stringFromJNI();
}
C++代码
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_qdian_testmyapp_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
java jni c++ 工作过程中常用的一些东西,在这里总结一下
获取field
env->getFieldID(class,"name",field类型);
获取method
env->getMethodID(class,"方法名",(参数)返回类型 );
还有三个特殊的string object arraylist
string---Ljava/lang/String
object---Ljava/lang/Object
array---Ljava/util/ArrayList
char *转jstring
JNIEnv* env,
std::string hello = "Hello from C++";
jstring str=env->NewStringUTF(hello.c_str());
jstring转char *
JNIEnv* env,
jboolean isCopy;
//evn:JNI 接口指针,data:Java 字符串对象,isCopy:指向布尔值的指针
char *startData = (*evn)->GetStringUTFChars(evn, data, &isCopy);
//使用了GetStringUTFChars一定要调用ReleaseStringChars函数释放资源
//env:JNI 接口指针,data:Java 字符串对象,指向UTF-8 字符串的指针
(*evn)->ReleaseStringUTFChars(evn,data,startData);
JNIEnv的使用在C和C++中的区别
2人收藏此文章, 我要收藏发表于1个月前(2012-10-09 18:59) , 已有 121次阅读 ,共 0个评论
对于JNIEnv *env来说,在C中调用:
(*env)->NewStringUTF(env, "Hello from JNI!");
而在C++中如果按照上述调用则会发生'base operand of '->' has non-pointer type '_JNIEnv''错误,需要如下调用:
env->NewStringUTF("Hello from JNI!");
JNIEXPORT jstring JNICALL getString(JNIEnv *evn, jobject jobject1, jstring data) {
jboolean isCopy;
//evn:JNI 接口指针,data:Java 字符串对象,isCopy:指向布尔值的指针
char *startData = (*evn)->GetStringUTFChars(evn, data, &isCopy);
char *endData="Hello";
int size1=strlen(startData);
int size2= strlen(endData);
char resultData[256];
int index=-1;
for(int k=0;k<size1;k++){
resultData[++index]=startData[k];
}
for(int k=0;k<size2;k++){
resultData[++index]=endData[k];
}
jstring result=(*evn)->NewStringUTF(evn,resultData);
//使用了GetStringUTFChars一定要调用ReleaseStringChars函数释放资源
//env:JNI 接口指针,data:Java 字符串对象,指向UTF-8 字符串的指针
(*evn)->ReleaseStringUTFChars(evn,data,startData);
return result;
}
jni 方法签名: {"getString","(Ljava/lang/String;)Ljava/lang/String;",(void*)getString},
java调用 getString("jni-") 返回:jni-hello
使用JNIEnv全局变量调用FindClass()等函数发生crash
jclass remotedesktopclass = sJniEnv->FindClass("com/xxx/xxx/xxx/xxx");
sJniEnv是一个全局变量,但其实不应该全局保存JNIEnv。
这是一个和thread紧密关联的变量,在其他线程中使用就会crash。
【解决方式】
JNIEXPORT void JNICALL Java_xx_xx_xx_xx_xxx(JNIEnv * jniEnv, jobject object, jstring jflowNum, jstring judid){
jniEnv->GetJavaVM(&javaVm);
jclass tmp = jniEnv->FindClass("com/nationsky/rc/util/RemoteDesktopUtil");
remotedesktopclass = (jclass)jniEnv->NewGlobalRef(tmp);
.............
static void keyPress(int keyCode){
JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, __null };
JNIEnv* jniEvn = __null;
int res = javaVm->AttachCurrentThread(&jniEvn, &args);
method = jniEvn->GetStaticMethodID(remotedesktopclass,
"pressHome", "()V");
使用JNIEnv全局变量调用FindClass()等函数发生crash
jclass remotedesktopclass = sJniEnv->FindClass("com/xxx/xxx/xxx/xxx");
sJniEnv是一个全局变量,但其实不应该全局保存JNIEnv。
这是一个和thread紧密关联的变量,在其他线程中使用就会crash。
【解决方式】
JNIEXPORT void JNICALL Java_xx_xx_xx_xx_xxx(JNIEnv * jniEnv, jobject object, jstring jflowNum, jstring judid){
jniEnv->GetJavaVM(&javaVm);
jclass tmp = jniEnv->FindClass("com/nationsky/rc/util/RemoteDesktopUtil");
remotedesktopclass = (jclass)jniEnv->NewGlobalRef(tmp);
.............
static void keyPress(int keyCode){
JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, __null };
JNIEnv* jniEvn = __null;
int res = javaVm->AttachCurrentThread(&jniEvn, &args);
method = jniEvn->GetStaticMethodID(remotedesktopclass,
"pressHome", "()V");
Qt控件内加控件
Qt5增强了很多安全性验证,如果出现setGeometry: Unable to set geometry,请将该控件的可见移到加入布局之后。
可以将控件A添加到布局,然后控件B设置该布局,这种灵活性大大提高了控件的组合度,比如可以在文本框左侧右侧增加一个搜索按钮,按钮设置图标即可。
QPushButton *btn = new QPushButton;
btn->resize(30, ui->lineEdit->height());
QHBoxLayout *layout = new QHBoxLayout(ui->lineEdit);
layout->setMargin(0);
layout->addStretch();
layout->addWidget(btn);
Qml控件占据剩余空间
Label {
id: titleLabel
text: listView.currentItem ? listView.currentItem.text : "Gallery"
font.pixelSize: 20
elide: Label.ElideRight
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
Layout.fillWidth: true
}
ToolButton
工具栏
ToolBar { //工具栏
RowLayout{ //横向
ToolButton{} //工具按钮
ToolButton{}
ToolButton{}
ToolButton{}
}
}
发布QML程序
windeployqt g:\HelloPlayer\HelloPlayer.exe --qmldir F:\QtAll\5.12.3\mingw73_32\qml
Qt播放视频报错 DirectShowPlayerService::doRender: Unresolved error code 0x80040266
下载并安装 LAVFilters:https://github.com/Nevcairiel/LAVFilters/releases
installer方式直接按步骤安装
压缩包方式解压右键以管理员运行 install_video.bat 文件
切记:需要重启电脑
完美,没毛病
播放器代码
QT += quick multimedia
CONFIG += c++11
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
import QtQuick 2.12
import QtMultimedia 5.0
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
//运行报错:Warning: "No decoder available for type 'video/x-h264, stream-format=(string)avc...
//sudo apt-get install gstreamer1.0-libav 即可解决 Qt在linux下使用gstreamer,Windows下使用directshow
ApplicationWindow {
width: 800
height: 600
title: "视频"
visible: true
property var date: new Date()
function parseTotalDuration(time) {
date.setTime(time)
if (time / 3600000 >= 1) {
//大于1小时
return Math.floor(time / 3600000) + ":" + Qt.formatDateTime(date,
"mm:ss")
} else {
return Qt.formatTime(date, "mm:ss")
}
}
Rectangle {
id: video
width: parent.width
height: 450
MediaPlayer {
id: mediaPlayer
source: "http://www.1xn1.com/upfiles/files/201912/2d67b6b5758c4098b8b8bd9534532b7c.mp4"
autoPlay: true
onError: {
console.log(errorString)
}
//position 属性 单位是毫秒,onPositionChanged 信号处理器,实时显示进度
onPositionChanged: {
progress.text = parseTotalDuration(
position) + progress.time
}
//duration 单位是毫秒,onDurationChanged 信号处理器,可以取到时长
onDurationChanged: {
progress.time = " / " + parseTotalDuration(duration)
}
onPlaybackStateChanged: {
switch (playbackState) {
case MediaPlayer.PlayingState:
state.text = "播放中"
break
case MediaPlayer.PausedState:
state.text = "已暂停"
break
case MediaPlayer.StoppedState:
state.text = "已结束"
break
}
}
//多媒体元信息
onStatusChanged: {
switch (status) {
case MediaPlayer.Loaded:
console.log(metaData.albumArtist, metaData.albumTitle,
metaData.author, metaData.channelCount)
break
}
}
}
VideoOutput {
anchors.fill: parent
source: mediaPlayer
}
MouseArea {
anchors.fill: parent
onDoubleClicked: {
if (mediaPlayer.playbackState === 2) {
mediaPlayer.play()
} else if (mediaPlayer.playbackState === 1) {
mediaPlayer.pause()
} else {
mediaPlayer.play()
}
}
}
}
Connections {
target: mediaPlayer
onStopped: {
}
}
Text {
id: progress
anchors.left: video.left
anchors.top: video.bottom
anchors.topMargin: 10
color: "#000000"
font.pointSize: 12
property string time
}
Text {
id: state
anchors {
verticalCenter: progress.verticalCenter
horizontalCenter: parent.horizontalCenter
}
color: "#000000"
font.pointSize: 12
}
Rectangle {
id: progressBar
anchors.left: parent.left
anchors.right: parent.right
anchors.top: progress.bottom
anchors.topMargin: 6
height: 6
radius: 3
color: "darkgray"
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
radius: 3
width: mediaPlayer.duration > 0 ? parent.width * mediaPlayer.position
/ mediaPlayer.duration : 0
color: "#4A6DBC"
}
MouseArea {
property int pos
anchors.fill: parent
onClicked: {
//seekable 属性指示媒体是否支持 seek,为true时,用seek(pos)方法来定位播放
if (mediaPlayer.seekable)
pos = mediaPlayer.duration * mouse.x / width
mediaPlayer.seek(pos)
}
}
}
}
qt android动态权限申请
前端时间要用qt开发一个android程序,结果发现在真机运行的时候莫名其妙无法加载写入文件,导致项目被搁置,今天重新研究了一下,终于搞定了,特此记录一下
工程中引入模块 androidextras
接下来项目中加入如下代码
bool MainWindow::requestPermission()
{
QtAndroid::PermissionResult r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
if(r == QtAndroid::PermissionResult::Denied) {
QtAndroid::requestPermissionsSync(QStringList()<<"android.permission.WRITE_EXTERNAL_STORAGE");
r = QtAndroid::checkPermission("android.permission.CAMERA");
if(r == QtAndroid::PermissionResult::Denied) {
return false;
}
}
return true;
}
使用权限前调用函数requestPermission();就可以了
但是里面要修改成相应权限
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_EXTERNAL_STORAGE
android.permission.CAMERA
另外手机内存卡的根目录就是/storage/emulated/0/
下一篇:项目实用代码收集