Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ local.properties
/app/videonative/compile_commands.json
/app/wfbngrtl8812/compile_commands.json
/compile_commands.json
/tmp
video.sdp
plan.md
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ where (brace yourself) most things actually work.
- [LiveVideo10ms](https://github.com/Consti10/LiveVideo10ms): excellent video decoder from [Consti10](https://github.com/Consti10) converted into a module.
- [wfb-ng](https://github.com/svpcom/wfb-ng): library allowing the broadcast of the video feed over the air.

The wfb-ng [gs.key](https://github.com/OpenIPC/PixelPilot/raw/main/app/src/main/assets/gs.key) is embedded in the app.
The wfb-ng [gs.key](https://github.com/OpenIPC/PixelPilot/raw/master/app/src/main/assets/gs.key) is embedded in the app.
The settings menu allows selecting a different key from your phone.

Supported rtl8812au wifi adapter are listed [here](https://github.com/OpenIPC/PixelPilot/blob/master/app/src/main/res/xml/usb_device_filter.xml).
Expand Down
86 changes: 86 additions & 0 deletions app/src/main/java/com/openipc/pixelpilot/VideoActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,9 @@ private void showSettingsMenu(View anchor) {
// Drone submenu
setupDroneSubMenu(popup);

// UDP Forwarding submenu
setupUdpForwardingSubMenu(popup);

// Help submenu
setupHelpSubMenu(popup);

Expand Down Expand Up @@ -920,6 +923,88 @@ private void setupDroneSubMenu(PopupMenu popup) {
});
}

private void setupUdpForwardingSubMenu(PopupMenu popup) {
SubMenu forwardMenu = popup.getMenu().addSubMenu("UDP Forwarding");

SharedPreferences prefs = getSharedPreferences("general", MODE_PRIVATE);
boolean enabled = prefs.getBoolean("forward_udp_enabled", false);
String ip = prefs.getString("forward_udp_ip", "192.168.1.100");
int port = prefs.getInt("forward_udp_port", 5600);

MenuItem enableItem = forwardMenu.add("Enable");
enableItem.setCheckable(true);
enableItem.setChecked(enabled);
enableItem.setOnMenuItemClickListener(item -> {
boolean newState = !item.isChecked();
item.setChecked(newState);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("forward_udp_enabled", newState);
editor.apply();
updateUdpForwardingState();
return true;
});

MenuItem configItem = forwardMenu.add("Config Target (" + ip + ":" + port + ")");
configItem.setOnMenuItemClickListener(item -> {
showUdpForwardingDialog();
return true;
});
}

private void showUdpForwardingDialog() {
SharedPreferences prefs = getSharedPreferences("general", MODE_PRIVATE);
String ip = prefs.getString("forward_udp_ip", "192.168.1.100");
int port = prefs.getInt("forward_udp_port", 5600);

android.widget.LinearLayout layout = new android.widget.LinearLayout(this);
layout.setOrientation(android.widget.LinearLayout.VERTICAL);
layout.setPadding(50, 30, 50, 30);

final android.widget.EditText ipEditText = new android.widget.EditText(this);
ipEditText.setHint("Destination IP Address");
ipEditText.setText(ip);
layout.addView(ipEditText);

final android.widget.EditText portEditText = new android.widget.EditText(this);
portEditText.setHint("Destination Port");
portEditText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
portEditText.setText(String.valueOf(port));
layout.addView(portEditText);

new android.app.AlertDialog.Builder(this)
.setTitle("UDP Forwarding Config")
.setView(layout)
.setPositiveButton("Save", (dialog, which) -> {
String newIp = ipEditText.getText().toString().trim();
String newPortStr = portEditText.getText().toString().trim();
int newPort = 5600;
try {
newPort = Integer.parseInt(newPortStr);
} catch (NumberFormatException ignored) {}

SharedPreferences.Editor editor = prefs.edit();
editor.putString("forward_udp_ip", newIp);
editor.putInt("forward_udp_port", newPort);
editor.apply();

Toast.makeText(this, "Forwarding settings saved: " + newIp + ":" + newPort, Toast.LENGTH_SHORT).show();
updateUdpForwardingState();
})
.setNegativeButton("Cancel", null)
.show();
}

private void updateUdpForwardingState() {
SharedPreferences prefs = getSharedPreferences("general", MODE_PRIVATE);
boolean enabled = prefs.getBoolean("forward_udp_enabled", false);
String ip = prefs.getString("forward_udp_ip", "192.168.1.100");
int port = prefs.getInt("forward_udp_port", 5600);

if (videoPlayer != null) {
videoPlayer.setUdpForwarding(ip, port, enabled);
}
}

/**
* Submenu for help items, such as sending logs.
*/
Expand Down Expand Up @@ -1358,6 +1443,7 @@ protected void onResume() {

wfbLinkManager.startAdapters();
videoPlayer.start();
updateUdpForwardingState();
videoPlayer.startAudio();

osdManager.restoreOSDConfig();
Expand Down
26 changes: 26 additions & 0 deletions app/videonative/src/main/cpp/UdpReceiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ void UDPReceiver::receiveFromUDPLoop()
// ssize_t message_length = recv(mSocket, buff, (size_t) mBuffsize, MSG_WAITALL);
if (message_length > 0)
{ // else -1 was returned;timeout/No data received
// 1. Forward packet first (minimize latency)
{
std::lock_guard<std::mutex> lock(mForwardMutex);
if (mForwardEnabled)
{
sendto(mSocket, buff->data(), message_length, 0, (struct sockaddr*) &mDestAddr, sizeof(mDestAddr));
}
}

// 2. Local processing
onDataReceivedCallback(buff->data(), (size_t) message_length);

nReceivedBytes += message_length;
Expand Down Expand Up @@ -162,3 +172,19 @@ int UDPReceiver::getPort() const
{
return mPort;
}

void UDPReceiver::setForwarding(const std::string& ip, int port, bool enabled)
{
std::lock_guard<std::mutex> lock(mForwardMutex);
mForwardIP = ip;
mForwardPort = port;
mForwardEnabled = enabled;

memset(&mDestAddr, 0, sizeof(mDestAddr));
mDestAddr.sin_family = AF_INET;
mDestAddr.sin_port = htons(port);
if (inet_pton(AF_INET, ip.c_str(), &mDestAddr.sin_addr) <= 0)
{
mForwardEnabled = false;
}
}
9 changes: 9 additions & 0 deletions app/videonative/src/main/cpp/UdpReceiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cstdio>
#include <iostream>
#include <thread>
#include <mutex>
// Starts a new thread that continuously checks for new data on UDP port

class UDPReceiver
Expand Down Expand Up @@ -64,6 +65,8 @@ class UDPReceiver

int getPort() const;

void setForwarding(const std::string& ip, int port, bool enabled);

private:
void receiveFromUDPLoop();

Expand All @@ -84,6 +87,12 @@ class UDPReceiver
// 65,507 bytes (65,535 − 8 byte UDP header − 20 byte IP header).
static constexpr const size_t UDP_PACKET_MAX_SIZE = 65507;
JavaVM* javaVm;

std::mutex mForwardMutex;
std::string mForwardIP = "";
int mForwardPort = 0;
bool mForwardEnabled = false;
struct sockaddr_in mDestAddr;
};

#endif // FPVUE_UDPRECEIVER_H
25 changes: 25 additions & 0 deletions app/videonative/src/main/cpp/VideoPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ void VideoPlayer::start(JNIEnv* env, jobject androidContext)
-16,
[this](const uint8_t* data, size_t data_length) { onNewRTPData(data, data_length); },
WANTED_UDP_RCVBUF_SIZE);
mUDPReceiver->setForwarding(mForwardIP, mForwardPort, mForwardEnabled);
mUDPReceiver->startReceiving();

mUDSReceiver.release();
Expand Down Expand Up @@ -250,6 +251,17 @@ void VideoPlayer::stopDvr()
stopProcessing();
}

void VideoPlayer::setForwarding(const std::string& ip, int port, bool enabled)
{
mForwardIP = ip;
mForwardPort = port;
mForwardEnabled = enabled;
if (mUDPReceiver)
{
mUDPReceiver->setForwarding(ip, port, enabled);
}
}

//----------------------------------------------------JAVA
// bindings---------------------------------------------------------------
#define JNI_METHOD(return_type, method_name) \
Expand Down Expand Up @@ -293,6 +305,19 @@ extern "C"
native(videoPlayerN)->stop(env, androidContext);
}

JNI_METHOD(void, nativeSetUdpForwarding)
(JNIEnv* env, jclass jclass1, jlong nativeInstance, jstring ipStr, jint port, jboolean enabled)
{
VideoPlayer* p = native(nativeInstance);
if (p)
{
const char* ip = env->GetStringUTFChars(ipStr, nullptr);
std::string ip_cpp(ip);
env->ReleaseStringUTFChars(ipStr, ip);
p->setForwarding(ip_cpp, port, enabled);
}
}

JNI_METHOD(void, nativeSetVideoSurface)
(JNIEnv* env, jclass jclass1, jlong videoPlayerN, jobject surface, jint index)
{
Expand Down
6 changes: 6 additions & 0 deletions app/videonative/src/main/cpp/VideoPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class VideoPlayer

bool isRecording() { return (get_time_ms() - last_dvr_write) <= 500; }

void setForwarding(const std::string& ip, int port, bool enabled);

private:
void onNewNALU(const NALU& nalu);

Expand Down Expand Up @@ -112,6 +114,10 @@ class VideoPlayer

void processQueue();

std::string mForwardIP = "";
int mForwardPort = 0;
bool mForwardEnabled = false;

public:
AudioDecoder audioDecoder;
VideoDecoder videoDecoder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public VideoPlayer(final AppCompatActivity parent) {

public static native void nativeSetVideoSurface(long nativeInstance, Surface surface, int index);

public static native void nativeSetUdpForwarding(long nativeInstance, String ip, int port, boolean enabled);

public static native void nativeStartDvr(long nativeInstance, int fd, int fmp4_enabled);

public static native void nativeStopDvr(long nativeInstance);
Expand Down Expand Up @@ -124,6 +126,11 @@ public boolean isRunning() {
return timer != null;
}

public void setUdpForwarding(String ip, int port, boolean enabled) {
verifyApplicationThread();
nativeSetUdpForwarding(nativeVideoPlayer, ip, port, enabled);
}

public void startDvr(int fd, boolean enabled_fmp4) {
nativeStartDvr(nativeVideoPlayer, fd, enabled_fmp4 ? 1 : 0);
}
Expand Down
Binary file added test_APK/app-debug.apk
Binary file not shown.