diff --git a/.gitignore b/.gitignore index 63a7bb0..05cfb4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -droidcam* +/droidcam +/droidcam-cli # vim swap files *.swp diff --git a/Makefile b/Makefile index f45cd20..0acc646 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # Use at your own risk. See README file for more details. -# # Variables with ?= can be changed during invocation # Example: # APPINDICATOR=ayatana-appindicator3-0.1 make droidcam @@ -20,7 +19,7 @@ GTK = `pkg-config --libs --cflags gtk+-3.0` `pkg-config --libs x11` GTK += `pkg-config --libs --cflags $(APPINDICATOR)` LIBAV = `pkg-config --libs --cflags libswscale libavutil` JPEG = `pkg-config --libs --cflags libturbojpeg` -USBMUXD = `pkg-config --libs --cflags libusbmuxd` +USBMUXD = `pkg-config --libs --cflags libusbmuxd-2.0` LIBS = -lspeex -lasound -lpthread -lm SRC = src/connection.c src/settings.c src/decoder*.c src/av.c src/usb.c src/queue.c diff --git a/src/av.c b/src/av.c index 184e3ab..84b3171 100644 --- a/src/av.c +++ b/src/av.c @@ -10,19 +10,22 @@ #include "settings.h" #include "connection.h" #include "decoder.h" +#include #include +#include +#include extern int a_active; extern int v_active; -extern int a_running; -extern int v_running; -extern int thread_cmd; +extern bool a_running; +extern bool v_running; +extern char thread_cmd; extern struct settings g_settings; const char *thread_cmd_val_str; SOCKET GetConnection(void) { - char *err; + const char *err; SOCKET socket = INVALID_SOCKET; if (g_settings.connection == CB_RADIO_IOS) { @@ -38,56 +41,81 @@ SOCKET GetConnection(void) { // Battry Check thread void *BatteryThreadProc(__attribute__((__unused__)) void *args) { SOCKET socket = INVALID_SOCKET; - char buf[128] = {0}; - char battery_value[32] = {0}; - int i, j; + char buf[96]; + char battery_value[5]; + struct timeval tv = {0}; + tv.tv_sec = 1; dbgprint("Battery Thread Start\n"); while (v_running || a_running) { - if (v_active == 0 && a_active == 0) { + if (!v_active && !a_active) { usleep(50000); continue; } + uint8_t battery_value_pos = 0; + size_t percent_len = 0; + socket = GetConnection(); if (socket == INVALID_SOCKET) { goto LOOP; } - if (Send(BATTERY_REQ, CSTR_LEN(BATTERY_REQ), socket) <= 0) { - errprint("error sending battery status request: (%d) '%s'\n", - errno, strerror(errno)); + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, + sizeof(tv)) == -1) { + goto LOOP; + }; + + if (!Send(BATTERY_REQ, CSTR_LEN(BATTERY_REQ), socket)) { + errprint("error sending battery status request: (%d) '%s'\n", errno, + strerror(errno)); goto LOOP; } memset(buf, 0, sizeof(buf)); + memset(battery_value, 0, sizeof(battery_value)); if (RecvAll(buf, sizeof(buf), socket) <= 0) { goto LOOP; } - for (i = 0; i < (sizeof(buf)-4); i++) { - if (buf[i] == '\r' && buf[i+1] == '\n' && buf[i+2] == '\r' && buf[i+3] == '\n') { - i += 4; + for (uint8_t i = 0; i < (sizeof(buf) - 4); i += 4) { + char *tmp = memchr(&buf[i], '\r', sizeof(buf)); + if (!tmp) { + goto LOOP; + } + if (memcmp(tmp + 1, "\n\r\n", 3) == 0) { + battery_value_pos = (tmp - buf) + 4; break; } } + if (!battery_value_pos || battery_value_pos > (sizeof(buf) - (4 + 1)) || + buf[battery_value_pos] == '0') { + goto LOOP; + } - j = 0; - while (i < sizeof(buf) && j < (sizeof(battery_value)-2) && buf[i] >= '0' && buf[i] <= '9') - battery_value[j++] = buf[i++]; + for (uint8_t j = 0; j < 3; j++) { + if (buf[battery_value_pos + j] >= '0' && + buf[battery_value_pos + j] <= '9') { + battery_value[j] = buf[battery_value_pos + j]; + percent_len++; + } else { + break; + } + } - if (j == 0) - battery_value[j++] = '-'; + if (percent_len == 0) { + battery_value[0] = '-'; + percent_len = 1; + } - battery_value[j++] = '%'; - battery_value[j++] = 0; + battery_value[percent_len] = '%'; dbgprint("battery_value: %s\n", battery_value); UpdateBatteryLabel(battery_value); LOOP: disconnect(socket); - for (j = 0; j < 30000 && (v_running || a_running); j++) + for (uint16_t i = 0; i < 30000 && (v_running || a_running); i++) usleep(1000); } @@ -97,7 +125,7 @@ void *BatteryThreadProc(__attribute__((__unused__)) void *args) { void *DecodeThreadProc(__attribute__((__unused__)) void *args) { dbgprint("Decode Thread Start\n"); - while (v_running != 0) { + while (v_running) { JPGFrame *f = pull_ready_jpg_frame(); if (!f) { usleep(2000); @@ -125,8 +153,7 @@ void *VideoThreadProc(void *args) { } len = snprintf(buf, sizeof(buf), VIDEO_REQ, decoder_get_video_width(), decoder_get_video_height()); - if (Send(buf, len, videoSocket) <= 0){ - errprint("send error (%d) '%s'\n", errno, strerror(errno)); + if (!Send(buf, len, videoSocket)){ MSG_ERROR("Error sending request, DroidCam might be busy with another client."); goto early_out; } @@ -143,9 +170,8 @@ void *VideoThreadProc(void *args) { } v_active = 1; - while (v_running != 0){ + while (v_running) { if (thread_cmd != 0) { - len = 0; if (thread_cmd == CB_CONTROL_WB) { len = snprintf(buf, sizeof(buf), OTHER_REQ_STR, thread_cmd, thread_cmd_val_str); } @@ -246,9 +272,8 @@ void *AudioThreadProc(void *arg) { return 0; } - if (Send(AUDIO_REQ, CSTR_LEN(AUDIO_REQ), socket) <= 0) { + if (!Send(AUDIO_REQ, CSTR_LEN(AUDIO_REQ), socket)) { errprint("send error (audio) (%d) '%s'\n", errno, strerror(errno)); - MSG_ERROR("Error sending audio request"); goto early_out; } @@ -276,7 +301,7 @@ void *AudioThreadProc(void *arg) { bytes_per_packet = CHUNKS_PER_PACKET * DROIDCAM_SPX_CHUNK_BYTES_2; STREAM: - a_active = 1; + a_active = true; while (a_running) { int len = (mode == UDP_STREAM) ? RecvNonBlockUDP(stream_buf, STREAM_BUF_SIZE, socket) @@ -339,7 +364,7 @@ void *AudioThreadProc(void *arg) { } early_out: - a_active = 0; + a_active = false; if (mode == UDP_STREAM) SendUDPMessage(socket, STOP_REQ, CSTR_LEN(STOP_REQ), g_settings.ip, g_settings.port + 1); diff --git a/src/connection.c b/src/connection.c index 958a147..2d898e4 100644 --- a/src/connection.c +++ b/src/connection.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,14 +21,14 @@ #include "connection.h" SOCKET wifiServerSocket = INVALID_SOCKET; -extern int v_running; +extern bool v_running; -char* DROIDCAM_CONNECT_ERROR = \ +const char* DROIDCAM_CONNECT_ERROR = \ "Connect failed, please try again.\n" "Check IP and Port.\n" "Check network connection.\n"; -SOCKET Connect(const char* ip, int port, char **errormsg) { +SOCKET Connect(const char* ip, int port, const char **errormsg) { int flags; struct sockaddr_in sin; SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -85,41 +86,41 @@ SOCKET Connect(const char* ip, int port, char **errormsg) { return sock; } -int Send(const char * buffer, int bytes, SOCKET s) { +bool Send(const char * buffer, int bytes, SOCKET s) { ssize_t w = 0; char *ptr = (char*) buffer; while (bytes > 0) { w = send(s, ptr, bytes, 0); if (w <= 0) { - return -1; + return false; } bytes -= w; ptr += w; } - return 1; + return true; } -int Recv(const char* buffer, int bytes, SOCKET s) { +ssize_t Recv(const char* buffer, int bytes, SOCKET s) { return recv(s, (char*)buffer, bytes, 0); } -int RecvAll(const char* buffer, int bytes, SOCKET s) { +ssize_t RecvAll(const char* buffer, int bytes, SOCKET s) { return recv(s, (char*)buffer, bytes, MSG_WAITALL); } -int RecvNonBlock(char * buffer, int bytes, SOCKET s) { +ssize_t RecvNonBlock(char * buffer, int bytes, SOCKET s) { int res = recv(s, buffer, bytes, MSG_DONTWAIT); return (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) ? 0 : res; } -int RecvNonBlockUDP(char * buffer, int bytes, SOCKET s) { +ssize_t RecvNonBlockUDP(char * buffer, int bytes, SOCKET s) { struct sockaddr_in from; socklen_t fromLen = sizeof(from); int res = recvfrom(s, buffer, bytes, MSG_DONTWAIT, (struct sockaddr *)&from, &fromLen); return (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) ? 0 : res; } -int SendUDPMessage(SOCKET s, const char *message, int length, char *ip, int port) { +ssize_t SendUDPMessage(SOCKET s, const char *message, int length, char *ip, int port) { struct sockaddr_in sin; sin.sin_port = htons((uint16_t)port); sin.sin_family = AF_INET; diff --git a/src/connection.h b/src/connection.h index e61eb63..f00afb8 100644 --- a/src/connection.h +++ b/src/connection.h @@ -9,21 +9,24 @@ #ifndef __CONN_H__ #define __CONN_H__ +#include +#include + #define INVALID_SOCKET -1 typedef int SOCKET; typedef long int SOCKET_PTR; -SOCKET Connect(const char* ip, int port, char **errormsg); +SOCKET Connect(const char* ip, int port, const char **errormsg); void connection_cleanup(); void disconnect(SOCKET s); SOCKET accept_connection(int port); SOCKET CreateUdpSocket(void); -int Send(const char * buffer, int bytes, SOCKET s); -int Recv(const char * buffer, int bytes, SOCKET s); -int RecvAll(const char * buffer, int bytes, SOCKET s); -int RecvNonBlock(char * buffer, int bytes, SOCKET s); -int RecvNonBlockUDP(char * buffer, int bytes, SOCKET s); -int SendUDPMessage(SOCKET s, const char *message, int length, char *ip, int port); +bool Send(const char * buffer, int bytes, SOCKET s); +ssize_t Recv(const char * buffer, int bytes, SOCKET s); +ssize_t RecvAll(const char * buffer, int bytes, SOCKET s); +ssize_t RecvNonBlock(char * buffer, int bytes, SOCKET s); +ssize_t RecvNonBlockUDP(char * buffer, int bytes, SOCKET s); +ssize_t SendUDPMessage(SOCKET s, const char *message, int length, char *ip, int port); #endif diff --git a/src/decoder.c b/src/decoder.c index 5c72ceb..e6613e4 100644 --- a/src/decoder.c +++ b/src/decoder.c @@ -93,10 +93,10 @@ int decoder_init(const char* v4l2_device, unsigned v4l2_width, unsigned v4l2_hei set_v4l2_device(v4l2_device); droidcam_device_fd = open_v4l2_device(); } else { - droidcam_device_fd = find_v4l2_device("platform:v4l2loopback_dc"); + droidcam_device_fd = find_v4l2_device("platform:v4l2loopback_dc", &WEBCAM_W, &WEBCAM_H); if (droidcam_device_fd < 0) { // check for generic v4l2loopback device - droidcam_device_fd = find_v4l2_device("platform:v4l2loopback"); + droidcam_device_fd = find_v4l2_device("platform:v4l2loopback", &WEBCAM_W, &WEBCAM_H); } } @@ -108,13 +108,6 @@ int decoder_init(const char* v4l2_device, unsigned v4l2_width, unsigned v4l2_hei WEBCAM_W = 320; WEBCAM_H = 240; droidcam_device_fd = 0; - } else { - query_v4l_device(droidcam_device_fd, &WEBCAM_W, &WEBCAM_H); - dbgprint("WEBCAM_W=%d, WEBCAM_H=%d\n", WEBCAM_W, WEBCAM_H); - if (WEBCAM_W < 2 || WEBCAM_H < 2 || WEBCAM_W > 9999 || WEBCAM_H > 9999){ - MSG_ERROR("Unable to query v4l2 device for correct parameters"); - return 0; - } } memset(&jpg_decoder, 0, sizeof(struct jpg_dec_ctx_s)); diff --git a/src/decoder.h b/src/decoder.h index 646776d..d675aa2 100644 --- a/src/decoder.h +++ b/src/decoder.h @@ -47,7 +47,7 @@ int decoder_horizontal_flip(); int decoder_vertical_flip(); void decoder_show_test_image(); -/* 20ms 16hkz 16 bit */ +/* 20ms 16khz 16 bit */ #define DROIDCAM_CHUNK_MS_2 20 #define DROIDCAM_SPX_CHUNK_BYTES_2 70 #define DROIDCAM_PCM_CHUNK_BYTES_2 640 @@ -65,7 +65,7 @@ void decoder_show_test_image(); void set_v4l2_device(const char* device); int open_v4l2_device(void); -int find_v4l2_device(const char* bus_info); +int find_v4l2_device(const char* bus_info, unsigned *in_v4l2_width, unsigned *in_v4l2_height); void query_v4l_device(int droidcam_device_fd, unsigned *WEBCAM_W, unsigned *WEBCAM_H); snd_pcm_t *find_snd_device(void); diff --git a/src/decoder_snd.c b/src/decoder_snd.c index fef0408..2f995f7 100644 --- a/src/decoder_snd.c +++ b/src/decoder_snd.c @@ -24,14 +24,12 @@ static snd_pcm_sframes_t period_size; static unsigned int period_time = PERIOD_TIME;/* period time in us */ static unsigned int buffer_time = DROIDCAM_SPEEX_BACKBUF_MAX_COUNT * PERIOD_TIME; /* ring buffer length in us */ -snd_pcm_hw_params_t *hwparams; -snd_pcm_sw_params_t *swparams; - static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access) { unsigned int rrate; snd_pcm_uframes_t size; - int err, dir; - int resample = 1; + int err; + int dir = 0; + const unsigned int resample = 1; /* choose all parameters */ err = snd_pcm_hw_params_any(handle, params); if (err < 0) { @@ -248,6 +246,9 @@ int snd_transfer_commit(snd_pcm_t *handle, struct snd_transfer_s *transfer) { snd_pcm_t *find_snd_device(void) { int err, card, i; snd_pcm_t *handle = NULL; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); diff --git a/src/decoder_v4l2.c b/src/decoder_v4l2.c index bea3b98..13fd3cc 100644 --- a/src/decoder_v4l2.c +++ b/src/decoder_v4l2.c @@ -32,7 +32,7 @@ int open_v4l2_device(void) { if (!S_ISCHR(st.st_mode)) return 0; - fd = open(v4l2_device, O_RDWR | O_NONBLOCK, 0); + fd = open(v4l2_device, O_WRONLY | O_NONBLOCK, 0); if (fd <= 0) { errprint("Error opening '%s': %d '%s'\n", v4l2_device, errno, strerror(errno)); return 0; @@ -42,7 +42,7 @@ int open_v4l2_device(void) { return fd; } -int find_v4l2_device(const char* bus_info) { +int find_v4l2_device(const char* bus_info, unsigned *in_v4l2_width, unsigned *in_v4l2_height) { int bus_info_len = strlen(bus_info); int video_dev_fd; int video_dev_nr = 0; @@ -50,6 +50,9 @@ int find_v4l2_device(const char* bus_info) { dbgprint("Looking for v4l2 card: %s\n", bus_info); for (video_dev_nr = 0; video_dev_nr < 99; video_dev_nr++) { + unsigned v4l2_width = *in_v4l2_width; + unsigned v4l2_height = *in_v4l2_height; + snprintf(v4l2_device, sizeof(v4l2_device), "/dev/video%d", video_dev_nr); video_dev_fd = open_v4l2_device(); @@ -61,8 +64,17 @@ int find_v4l2_device(const char* bus_info) { continue; } + query_v4l_device(video_dev_fd, &v4l2_width, &v4l2_height) dbgprint("Device %s is '%s' @ %s\n", v4l2_device, v4l2cap.card, v4l2cap.bus_info); + dbgprint("v4l2_width=%d, v4l2_height=%d\n", v4l2_width, v4l2_height); + if (v4l2_width < 2 || v4l2_height < 2 || v4l2_width > 9999 || v4l2_height > 9999){ + errprint("Unable to query v4l2 device for correct parameters\n"); + continue; + } + if (0 == strncmp(bus_info, (const char*) v4l2cap.bus_info, bus_info_len)) { + *in_v4l2_width = v4l2_width; + *in_v4l2_height = v4l2_height; return video_dev_fd; } diff --git a/src/droidcam-cli.c b/src/droidcam-cli.c index b1435a8..02b335b 100644 --- a/src/droidcam-cli.c +++ b/src/droidcam-cli.c @@ -8,6 +8,8 @@ #include #include +#include +#include #include "common.h" #include "settings.h" @@ -23,11 +25,11 @@ Thread athread = {0, -1}, vthread = {0, -1}, dthread = {0, -1}; char *v4l2_dev = 0; unsigned v4l2_width = 0, v4l2_height = 0; -volatile int a_active = 0; -volatile int v_active = 0; -volatile int v_running = 0; -volatile int a_running = 0; -volatile int thread_cmd = 0; +volatile bool a_active = false; +volatile bool v_active = false; +volatile bool v_running = false; +volatile bool a_running = false; +volatile char thread_cmd = 0; int no_controls = 0; struct settings g_settings = {0}; @@ -39,8 +41,8 @@ void * VideoThreadProc(void * args); void * DecodeThreadProc(void * args); void sig_handler(__attribute__((__unused__)) int sig) { - a_running = 0; - v_running = 0; + a_running = false; + v_running = false; return; } @@ -113,7 +115,7 @@ static void parse_args(int argc, char *argv[]) { continue; } if (argv[i][0] == '-' && argv[i][1] == 'v') { - v_running = 1; + v_running = true; continue; } @@ -142,8 +144,8 @@ static void parse_args(int argc, char *argv[]) { if (argv[i][0] == '-' && argv[i][1] == 'l') { g_settings.port = strtoul(argv[i+1], NULL, 10); g_settings.connection = CB_WIFI_SRVR; - a_running = 0; - v_running = 1; + a_running = false; + v_running = true; return; } @@ -235,7 +237,7 @@ int main(int argc, char *argv[]) { parse_args(argc, argv); if (!v_running && !a_running) - v_running = 1; + v_running = true; if (!decoder_init(v4l2_dev, v4l2_width, v4l2_height)) { return 2; @@ -264,7 +266,7 @@ int main(int argc, char *argv[]) { videoSocket = rc; } else { - char *errmsg = NULL; + const char *errmsg = NULL; videoSocket = Connect(g_settings.ip, g_settings.port, &errmsg); if (videoSocket == INVALID_SOCKET) { errprint("Video: Connect failed to %s:%d\n", g_settings.ip, g_settings.port); diff --git a/src/droidcam.c b/src/droidcam.c index 851ee00..6dba68a 100644 --- a/src/droidcam.c +++ b/src/droidcam.c @@ -14,6 +14,7 @@ #endif #include +#include #include #include "common.h" @@ -40,11 +41,11 @@ GThread* hDecodeThread; GThread* hBatteryThread; char *v4l2_dev = 0; -volatile int a_active = 0; -volatile int v_active = 0; -volatile int a_running = 0; -volatile int v_running = 0; -volatile int thread_cmd = 0; +volatile bool a_active = false; +volatile bool v_active = false; +volatile bool v_running = false; +volatile bool a_running = false; +volatile char thread_cmd = 0; struct settings g_settings = {0}; extern const char *thread_cmd_val_str; @@ -105,9 +106,9 @@ void UpdateBatteryLabel(char *battery_value) { gtk_label_set_text(GTK_LABEL(batteryText), battery_value); } -static void Stop(void) { - a_running = 0; - v_running = 0; +static void stop_av(void) { + a_running = false; + v_running = false; dbgprint("join\n"); if (hVideoThread) { g_thread_join(hVideoThread); @@ -125,9 +126,9 @@ static void Stop(void) { g_thread_join(hBatteryThread); hBatteryThread = NULL; } +} - a_active = 0; - v_active = 0; +static void reset_ui(void) { gtk_widget_set_sensitive(GTK_WIDGET(elButton), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(wbButton), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(menuButton), FALSE); @@ -146,7 +147,7 @@ static void Start(void) { g_settings.port = port; if (g_settings.connection == CB_WIFI_SRVR) { - v_running = 1; + v_running = true; hVideoThread = g_thread_new(NULL, VideoThreadProc, (void*) (SOCKET_PTR) s); hDecodeThread = g_thread_new(NULL, DecodeThreadProc, NULL); goto EARLY_OUT; @@ -185,7 +186,7 @@ static void Start(void) { return; } - char *errmsg = NULL; + const char *errmsg = NULL; gtk_button_set_label(start_button, "Please wait"); s = Connect(ip, port, &errmsg); if (s == INVALID_SOCKET) { @@ -198,8 +199,8 @@ static void Start(void) { } if (g_settings.video) { - v_active = 0; - v_running = 1; + v_active = false; + v_running = true; hVideoThread = g_thread_new(NULL, VideoThreadProc, (void*) (SOCKET_PTR) s); hDecodeThread = g_thread_new(NULL, DecodeThreadProc, NULL); } else { @@ -207,8 +208,8 @@ static void Start(void) { } if (g_settings.audio) { - a_active = 0; - a_running = 1; + a_active = false; + a_running = true; hAudioThread = g_thread_new(NULL, AudioThreadProc, NULL); } @@ -259,7 +260,8 @@ static void the_callback(GtkWidget* widget, gpointer extra) switch (cb) { case CB_BUTTON: if (v_running || a_running) { - Stop(); + reset_ui(); + stop_av(); cb = (int)g_settings.connection; goto _up; } @@ -668,7 +670,7 @@ int main(int argc, char *argv[]) g_signal_connect(window, "delete-event", G_CALLBACK(delete_window_callback), window); gtk_widget_show_all(window); - Stop(); // reset the UI + reset_ui(); LoadSettings(&g_settings); if (argc >= 1) { parse_args(argc, argv); @@ -715,7 +717,7 @@ int main(int argc, char *argv[]) // main loop gtk_main(); - Stop(); + stop_av(); decoder_fini(); connection_cleanup(); SaveSettings(&g_settings); diff --git a/v4l2loopback/test.c b/v4l2loopback/test.c index e780826..f985bb3 100644 --- a/v4l2loopback/test.c +++ b/v4l2loopback/test.c @@ -112,7 +112,7 @@ int main(int argc, char**argv) printf("using output device: %s\n", video_device); } - fdwr = open(video_device, O_RDWR); + fdwr = open(video_device, O_WRONLY); assert(fdwr >= 0); ret_code = ioctl(fdwr, VIDIOC_QUERYCAP, &vid_caps);