Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
|
af57c97961 | 5 months ago |
|
12e8cb37e6 | 5 months ago |
|
c7f98851ca | 5 months ago |
|
e27435e9a4 | 5 months ago |
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// Created by Matthew on 2025/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "rtmp/rtmpsuck.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
extern STREAMING_SERVER *rtmpServer;
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_com_xypower_mpapp_RtmpService_startService(JNIEnv *env, jobject thiz) {
|
||||||
|
// TODO: implement startService()
|
||||||
|
RtmpSuckMain(0);
|
||||||
|
return (jlong)rtmpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_com_xypower_mpapp_RtmpService_stopService(JNIEnv *env, jobject thiz, jlong native_handle) {
|
||||||
|
stopStreaming(rtmpServer);
|
||||||
|
// free(rtmpServer);
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// Created by Matthew on 2025/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef MICROPHOTO_RTMPSUCK_H
|
||||||
|
#define MICROPHOTO_RTMPSUCK_H
|
||||||
|
|
||||||
|
#include <librtmp/rtmp_sys.h>
|
||||||
|
#include <librtmp/log.h>
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STREAMING_ACCEPTING,
|
||||||
|
STREAMING_IN_PROGRESS,
|
||||||
|
STREAMING_STOPPING,
|
||||||
|
STREAMING_STOPPED
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Flist
|
||||||
|
{
|
||||||
|
struct Flist *f_next;
|
||||||
|
FILE *f_file;
|
||||||
|
AVal f_path;
|
||||||
|
} Flist;
|
||||||
|
|
||||||
|
typedef struct Plist
|
||||||
|
{
|
||||||
|
struct Plist *p_next;
|
||||||
|
RTMPPacket p_pkt;
|
||||||
|
} Plist;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int sockfd;
|
||||||
|
int state;
|
||||||
|
uint32_t stamp;
|
||||||
|
RTMP rs;
|
||||||
|
RTMP rc;
|
||||||
|
Plist *rs_pkt[2]; /* head, tail */
|
||||||
|
Plist *rc_pkt[2]; /* head, tail */
|
||||||
|
Flist *f_head, *f_tail;
|
||||||
|
Flist *f_cur;
|
||||||
|
|
||||||
|
} STREAMING_SERVER;
|
||||||
|
|
||||||
|
STREAMING_SERVER *startStreaming(const char *address, int port);
|
||||||
|
void stopStreaming(STREAMING_SERVER * server);
|
||||||
|
|
||||||
|
int RtmpSuckMain(int logAll);
|
||||||
|
|
||||||
|
#endif //MICROPHOTO_RTMPSUCK_H
|
@ -0,0 +1,58 @@
|
|||||||
|
/* Thread compatibility glue
|
||||||
|
* Copyright (C) 2009 Howard Chu
|
||||||
|
*
|
||||||
|
* This Program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This Program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with RTMPDump; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "thread.h"
|
||||||
|
#include <librtmp/log.h>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
HANDLE
|
||||||
|
ThreadCreate(thrfunc *routine, void *args)
|
||||||
|
{
|
||||||
|
HANDLE thd;
|
||||||
|
|
||||||
|
thd = (HANDLE) _beginthread(routine, 0, args);
|
||||||
|
if (thd == -1L)
|
||||||
|
RTMP_LogPrintf("%s, _beginthread failed with %d\n", __FUNCTION__, errno);
|
||||||
|
|
||||||
|
return thd;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
pthread_t
|
||||||
|
ThreadCreate(thrfunc *routine, void *args)
|
||||||
|
{
|
||||||
|
pthread_t id = 0;
|
||||||
|
pthread_attr_t attributes;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pthread_attr_init(&attributes);
|
||||||
|
pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
|
||||||
|
|
||||||
|
ret =
|
||||||
|
pthread_create(&id, &attributes, routine, args);
|
||||||
|
if (ret != 0)
|
||||||
|
RTMP_LogPrintf("%s, pthread_create failed with %d\n", __FUNCTION__, ret);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,40 @@
|
|||||||
|
/* Thread compatibility glue
|
||||||
|
* Copyright (C) 2009 Howard Chu
|
||||||
|
*
|
||||||
|
* This Program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This Program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with RTMPDump; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __THREAD_H__
|
||||||
|
#define __THREAD_H__ 1
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include <process.h>
|
||||||
|
#define TFTYPE void
|
||||||
|
#define TFRET()
|
||||||
|
#define THANDLE HANDLE
|
||||||
|
#else
|
||||||
|
#include <pthread.h>
|
||||||
|
#define TFTYPE void *
|
||||||
|
#define TFRET() return 0
|
||||||
|
#define THANDLE pthread_t
|
||||||
|
#endif
|
||||||
|
typedef TFTYPE (thrfunc)(void *arg);
|
||||||
|
|
||||||
|
THANDLE ThreadCreate(thrfunc *routine, void *args);
|
||||||
|
#endif /* __THREAD_H__ */
|
@ -0,0 +1,230 @@
|
|||||||
|
package com.xypower.mpapp;
|
||||||
|
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class RtmpService extends Service {
|
||||||
|
public RtmpService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.loadLibrary("rtmpdump");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String TAG = "RTMP";
|
||||||
|
|
||||||
|
public static final String ACTION_START = "com.xypower.mprtmp.ACT_START";
|
||||||
|
public static final String ACTION_STOP = "com.xypower.mprtmp.ACT_STOP";
|
||||||
|
public static final String ACTION_MAIN = "com.xypower.mprtmp.ACT_MAIN";
|
||||||
|
|
||||||
|
public static final int NOTIFICATION_ID_FOREGROUND_SERVICE = 8466603;
|
||||||
|
|
||||||
|
private static final String FOREGROUND_CHANNEL_ID = "fg_rtmp";
|
||||||
|
|
||||||
|
public static class STATE_SERVICE {
|
||||||
|
public static final int CONNECTED = 10;
|
||||||
|
public static final int NOT_CONNECTED = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int mStateService = STATE_SERVICE.NOT_CONNECTED;
|
||||||
|
|
||||||
|
private NotificationManager mNotificationManager;
|
||||||
|
private ScreenActionReceiver mScreenaActionReceiver = null;
|
||||||
|
|
||||||
|
private Handler mHander = null;
|
||||||
|
private Thread mServiceThread;
|
||||||
|
private long mNativeHandle = 0;
|
||||||
|
|
||||||
|
static AtomicInteger reqCode = new AtomicInteger(100);
|
||||||
|
|
||||||
|
private native long startService();
|
||||||
|
private native void stopService(long nativeHandle);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
// TODO: Return the communication channel to the service.
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
mHander = new Handler();
|
||||||
|
|
||||||
|
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||||
|
mStateService = STATE_SERVICE.NOT_CONNECTED;
|
||||||
|
|
||||||
|
mScreenaActionReceiver = new ScreenActionReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
|
||||||
|
mStateService = STATE_SERVICE.NOT_CONNECTED;
|
||||||
|
|
||||||
|
unregisterReceiver(mScreenaActionReceiver);
|
||||||
|
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
|
||||||
|
if (intent == null) {
|
||||||
|
stopForeground(true);
|
||||||
|
stopSelf();
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if user starts the service
|
||||||
|
switch (intent.getAction()) {
|
||||||
|
case ACTION_START:
|
||||||
|
Log.d(TAG, "Received user starts foreground intent");
|
||||||
|
startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
|
||||||
|
|
||||||
|
connect();
|
||||||
|
|
||||||
|
registerReceiver(mScreenaActionReceiver, mScreenaActionReceiver.getFilter());
|
||||||
|
|
||||||
|
if (mServiceThread == null) {
|
||||||
|
|
||||||
|
mServiceThread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
mNativeHandle = startService();
|
||||||
|
|
||||||
|
Log.d(TAG, "RTMP service finishes");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mServiceThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ACTION_STOP:
|
||||||
|
unregisterReceiver(mScreenaActionReceiver);
|
||||||
|
|
||||||
|
stopForeground(true);
|
||||||
|
stopSelf();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
stopForeground(true);
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connect() {
|
||||||
|
// after 10 seconds its connected
|
||||||
|
mHander.postDelayed(
|
||||||
|
new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
// Log.d(TAG, "Bluetooth Low Energy device is connected!!");
|
||||||
|
Toast.makeText(getApplicationContext(), "RTMP Connected!", Toast.LENGTH_SHORT).show();
|
||||||
|
mStateService = STATE_SERVICE.CONNECTED;
|
||||||
|
startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Notification prepareNotification() {
|
||||||
|
// handle build version above android oreo
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O &&
|
||||||
|
mNotificationManager.getNotificationChannel(FOREGROUND_CHANNEL_ID) == null) {
|
||||||
|
CharSequence name = getString(R.string.text_name_notification);
|
||||||
|
int importance = NotificationManager.IMPORTANCE_DEFAULT;
|
||||||
|
NotificationChannel channel = new NotificationChannel(FOREGROUND_CHANNEL_ID, name, importance);
|
||||||
|
channel.enableVibration(false);
|
||||||
|
mNotificationManager.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||||
|
notificationIntent.setAction(ACTION_MAIN);
|
||||||
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
|
||||||
|
// if min sdk goes below honeycomb
|
||||||
|
/*if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
} else {
|
||||||
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int uniqueReqCode = reqCode.getAndIncrement();
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, uniqueReqCode, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
|
// make a stop intent
|
||||||
|
Intent stopIntent = new Intent(this, RtmpService.class);
|
||||||
|
stopIntent.setAction(ACTION_STOP);
|
||||||
|
uniqueReqCode = reqCode.getAndIncrement();
|
||||||
|
PendingIntent pendingStopIntent = PendingIntent.getService(this, uniqueReqCode, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.notification);
|
||||||
|
remoteViews.setOnClickPendingIntent(R.id.btn_stop, pendingStopIntent);
|
||||||
|
|
||||||
|
// if it is connected
|
||||||
|
switch (mStateService) {
|
||||||
|
case STATE_SERVICE.NOT_CONNECTED:
|
||||||
|
remoteViews.setTextViewText(R.id.tv_state, "DISCONNECTED");
|
||||||
|
break;
|
||||||
|
case STATE_SERVICE.CONNECTED:
|
||||||
|
remoteViews.setTextViewText(R.id.tv_state, "CONNECTED");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification builder
|
||||||
|
NotificationCompat.Builder notificationBuilder;
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||||
|
notificationBuilder = new NotificationCompat.Builder(this, FOREGROUND_CHANNEL_ID);
|
||||||
|
} else {
|
||||||
|
notificationBuilder = new NotificationCompat.Builder(this);
|
||||||
|
}
|
||||||
|
notificationBuilder
|
||||||
|
.setContent(remoteViews)
|
||||||
|
.setSmallIcon(R.drawable.ic_rtmpsuck)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
|
.setOnlyAlertOnce(true)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setContentIntent(pendingIntent);
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
return notificationBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stopRtmpService(Context context) {
|
||||||
|
|
||||||
|
Intent alarmIntent = new Intent();
|
||||||
|
|
||||||
|
alarmIntent.setPackage(context.getPackageName());
|
||||||
|
alarmIntent.setAction(ACTION_STOP);
|
||||||
|
int uniqueReqCode = reqCode.getAndIncrement();
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), uniqueReqCode, alarmIntent, 0);
|
||||||
|
|
||||||
|
AlarmManager alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(ALARM_SERVICE);
|
||||||
|
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 100, pendingIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#00FF00"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M20,4h-3.17L15,2L9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,6h4.05l1.83,-2h4.24l1.83,2L20,6v12zM12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zM12,15c-1.65,0 -3,-1.35 -3,-3s1.35,-3 3,-3 3,1.35 3,3 -1.35,3 -3,3z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#07EEB8"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FF0000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||||
|
</vector>
|
Loading…
Reference in New Issue