From 808225629721c2f7d5c751edc60e5c6744be7886 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 20 Sep 2014 11:53:40 +0200 Subject: First (crashing) prototype. --- configure.ac | 2 +- src/Makefile.am | 14 ++++- src/audiohandler.cc | 52 ------------------- src/audiohandler.h | 54 -------------------- src/audioinput.cc | 6 +-- src/audioinputhandler.cc | 55 ++++++++++++++++++++ src/audioinputhandler.h | 54 ++++++++++++++++++++ src/audiooutputhandler.cc | 38 ++++++++++++++ src/audiooutputhandler.h | 48 ++++++++++++++++++ src/frame.cc | 25 +++++++++ src/frame.h | 24 ++++++++- src/inputstreamer.cc | 122 ++++++++++++++++++++++++++++++++++++++++++++ src/inputstreamer.h | 65 ++++++++++++++++++++++++ src/mainwindow.cc | 67 ++++++++++++++++++++---- src/mainwindow.h | 18 +++++-- src/opusdecoder.cc | 53 +++++++++++++++++++ src/opusdecoder.h | 48 ++++++++++++++++++ src/opusencoder.cc | 70 ++++++------------------- src/opusencoder.h | 9 ++-- src/outputstreamer.cc | 127 ++++++++++++++++++++++++++++++++++++++++++++++ src/outputstreamer.h | 62 ++++++++++++++++++++++ src/simplertp.cc | 4 +- src/soundplayer.cc | 123 ++++++++++++++++++++++++++++++++++++++++++++ src/soundplayer.h | 65 ++++++++++++++++++++++++ src/v4l.cc | 10 +++- src/v4l.h | 5 +- 26 files changed, 1031 insertions(+), 189 deletions(-) delete mode 100644 src/audiohandler.cc delete mode 100644 src/audiohandler.h create mode 100644 src/audioinputhandler.cc create mode 100644 src/audioinputhandler.h create mode 100644 src/audiooutputhandler.cc create mode 100644 src/audiooutputhandler.h create mode 100644 src/inputstreamer.cc create mode 100644 src/inputstreamer.h create mode 100644 src/opusdecoder.cc create mode 100644 src/opusdecoder.h create mode 100644 src/outputstreamer.cc create mode 100644 src/outputstreamer.h create mode 100644 src/soundplayer.cc create mode 100644 src/soundplayer.h diff --git a/configure.ac b/configure.ac index 36978fd..2374032 100644 --- a/configure.ac +++ b/configure.ac @@ -31,7 +31,7 @@ PKG_PROG_PKG_CONFIG(0.23) dnl ====================== dnl Check for Qt dnl ====================== -PKG_CHECK_MODULES(QT, QtCore QtGui QtXml >= 4.5) +PKG_CHECK_MODULES(QT, QtCore QtGui QtNetwork >= 4.5) AC_CHECK_PROGS(QT_MOC, [moc4 moc-qt4 moc], []) AC_CHECK_PROGS(QT_RCC, [rcc4 rcc-qt4 rcc], []) AC_CHECK_PROGS(QT_UIC, [uic4 uic-qt4 uic], []) diff --git a/src/Makefile.am b/src/Makefile.am index 1b457ca..27ff019 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,20 +18,30 @@ simplertp_SOURCES = \ mainwindow.cc \ v4l.cc \ opusencoder.cc \ + opusdecoder.cc \ samplecache.cc \ frame.cc \ audioinput.cc \ - audiohandler.cc + audioinputhandler.cc \ + audiooutputhandler.cc \ + soundplayer.cc \ + outputstreamer.cc \ + inputstreamer.cc EXTRA_DIST = \ simplertp.qrc mainwindow.h \ v4l.h \ opusencoder.h \ + opusdecoder.h \ samplecache.h \ frame.h \ audioinput.h \ - audiohandler.h + audioinputhandler.h \ + audiooutputhandler.h \ + soundplayer.h \ + outputstreamer.h \ + inputstreamer.h simplertp_MOC = $(shell ../tools/MocList cc ) diff --git a/src/audiohandler.cc b/src/audiohandler.cc deleted file mode 100644 index 8deb414..0000000 --- a/src/audiohandler.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/*************************************************************************** - * audiohandler.cc - * - * Fri Sep 19 19:58:05 CEST 2014 - * Copyright 2014 Bent Bisballe Nyeng - * deva@aasimon.org - ****************************************************************************/ - -/* - * This file is part of SimpleRTP. - * - * SimpleRTP 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 of the License, or - * (at your option) any later version. - * - * SimpleRTP 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 SimpleRTP; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ -#include "audiohandler.h" - -AudioHandler::AudioHandler(QString device) - : ai(device.toStdString().c_str()) -{ - start(); -} - -AudioHandler::~AudioHandler() -{ - running = false; - wait(); -} - -void AudioHandler::run() -{ - char pcm[4096]; - running = true; - - while(running) { - int sz = ai.getSamples(pcm, sizeof(pcm)); - printf("sz: %d\n", sz); - framelist_t fl = oe.encode(pcm, sz); - emit newAudio(fl); - } -} diff --git a/src/audiohandler.h b/src/audiohandler.h deleted file mode 100644 index b61cfef..0000000 --- a/src/audiohandler.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/*************************************************************************** - * audiohandler.h - * - * Fri Sep 19 19:58:05 CEST 2014 - * Copyright 2014 Bent Bisballe Nyeng - * deva@aasimon.org - ****************************************************************************/ - -/* - * This file is part of SimpleRTP. - * - * SimpleRTP 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 of the License, or - * (at your option) any later version. - * - * SimpleRTP 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 SimpleRTP; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ -#ifndef __SIMPLERTP_AUDIOHANDLER_H__ -#define __SIMPLERTP_AUDIOHANDLER_H__ - -#include -#include - -#include "audioinput.h" -#include "opusencoder.h" - -class AudioHandler : public QThread { -Q_OBJECT -public: - AudioHandler(QString device); - ~AudioHandler(); - - void run(); - -signals: - void newAudio(framelist_t list); - -private: - AudioInput ai; - OpusEncoder oe; - - volatile bool running; -}; - -#endif/*__SIMPLERTP_AUDIOHANDLER_H__*/ diff --git a/src/audioinput.cc b/src/audioinput.cc index 8197193..cc68e4b 100644 --- a/src/audioinput.cc +++ b/src/audioinput.cc @@ -32,20 +32,20 @@ AudioInput::AudioInput(const char *device) { int err; ai = ai_init(&err, device, "", 48000, 1); - printf("ai_err: %d\n", err); + if(err) printf("ai_err: %d\n", err); } AudioInput::~AudioInput() { int err; ai_close(&err, ai); - printf("ai_err: %d\n", err); + if(err) printf("ai_err: %d\n", err); } int AudioInput::getSamples(void *pcm, size_t size) { int err; int ret = ai_read(&err, ai, pcm, size); - printf("ai_err: %d\n", err); + if(err) printf("ai_err: %d\n", err); return ret; } diff --git a/src/audioinputhandler.cc b/src/audioinputhandler.cc new file mode 100644 index 0000000..29af3cb --- /dev/null +++ b/src/audioinputhandler.cc @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * audioinputhandler.cc + * + * Sat Sep 20 11:03:46 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "audioinputhandler.h" + +#include + +AudioInputHandler::AudioInputHandler(QString device) + : ai(device.toStdString().c_str()) +{ + qRegisterMetaType("framelist_t"); + start(); +} + +AudioInputHandler::~AudioInputHandler() +{ + running = false; + wait(); +} + +void AudioInputHandler::run() +{ + char pcm[4096]; + running = true; + + while(running) { + int sz = ai.getSamples(pcm, sizeof(pcm)); + // printf("sz: %d\n", sz); + framelist_t fl = oe.encode(pcm, sz); + if(fl.size()) emit newAudio(fl); + } +} diff --git a/src/audioinputhandler.h b/src/audioinputhandler.h new file mode 100644 index 0000000..dbf43e7 --- /dev/null +++ b/src/audioinputhandler.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * audioinputhandler.h + * + * Sat Sep 20 11:03:46 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __SIMPLERTP_AUDIOINPUTHANDLER_H__ +#define __SIMPLERTP_AUDIOINPUTHANDLER_H__ + +#include +#include + +#include "audioinput.h" +#include "opusencoder.h" + +class AudioInputHandler : public QThread { +Q_OBJECT +public: + AudioInputHandler(QString device); + ~AudioInputHandler(); + + void run(); + +signals: + void newAudio(framelist_t list); + +private: + AudioInput ai; + OpusEncoder oe; + + volatile bool running; +}; + +#endif/*__SIMPLERTP_AUDIOINPUTHANDLER_H__*/ diff --git a/src/audiooutputhandler.cc b/src/audiooutputhandler.cc new file mode 100644 index 0000000..6a5056f --- /dev/null +++ b/src/audiooutputhandler.cc @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * audiooutputhandler.cc + * + * Sat Sep 20 11:03:53 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "audiooutputhandler.h" + +AudioOutputHandler::AudioOutputHandler() +{ +} + +void AudioOutputHandler::newAudio(Frame frame) +{ + char pcm[10000]; + int ret = decoder.decode(frame, pcm, sizeof(pcm)); + if(ret > 0) player.playSamples(pcm, ret); +} diff --git a/src/audiooutputhandler.h b/src/audiooutputhandler.h new file mode 100644 index 0000000..ff2054e --- /dev/null +++ b/src/audiooutputhandler.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * audiooutputhandler.h + * + * Sat Sep 20 11:03:52 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __SIMPLERTP_AUDIOOUTPUTHANDLER_H__ +#define __SIMPLERTP_AUDIOOUTPUTHANDLER_H__ + +#include + +#include "soundplayer.h" +#include "opusdecoder.h" + +class AudioOutputHandler : public QObject { +Q_OBJECT +public: + AudioOutputHandler(); + +public slots: + void newAudio(Frame frame); + +private: + SoundPlayer player; + OpusDecoder decoder; +}; + +#endif/*__SIMPLERTP_AUDIOOUTPUTHANDLER_H__*/ diff --git a/src/frame.cc b/src/frame.cc index 4801897..38d6f0f 100644 --- a/src/frame.cc +++ b/src/frame.cc @@ -29,8 +29,33 @@ #include #include +static unsigned long int ts_cnt = 0; + +Frame::Frame() +{ + data = NULL; + size = 0; + ts = 0; +} + +Frame::Frame(const Frame &frame) +{ + data = frame.data; + size = frame.size; + ts = frame.ts; +} + Frame::Frame(size_t s) { size = s; data = (char*)malloc(size); + ts = ts_cnt++; +} + +Frame::Frame(const char *d, size_t s) +{ + size = s; + data = (char*)malloc(size); + memcpy(data, d, s); + ts = ts_cnt++; } diff --git a/src/frame.h b/src/frame.h index f5faebd..eb5cebe 100644 --- a/src/frame.h +++ b/src/frame.h @@ -29,12 +29,34 @@ #include #include +#include -class Frame { +class Frame : public QObject { +Q_OBJECT public: + /** + * Empty fram, no data, size == 0 + */ + Frame(); + + /** + * Copy constructor, take sover pointer + */ + Frame(const Frame &frame); + + /** + * Allocate buffer + */ Frame(size_t s); + + /** + * Allocate new buffer and copy data + */ + Frame(const char *data, size_t size); + char *data; size_t size; + unsigned long int ts; }; typedef QVector< Frame* > framelist_t; diff --git a/src/inputstreamer.cc b/src/inputstreamer.cc new file mode 100644 index 0000000..a1d7aa9 --- /dev/null +++ b/src/inputstreamer.cc @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * inputstreamer.cc + * + * Sat Sep 20 10:15:51 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "inputstreamer.h" + +#define KEY "123456789012345678901234567890123456789012345678901234567890" +#define SSRC 1234567890 +#define CSRC_V 42 +#define CSRC_A 43 + +InputStreamer::InputStreamer(QHostAddress addr, quint16 port) + : socket(this) +{ + total = 0; + + socket.bind(port); + this->addr = addr; + this->port = port; + + running = true; + start(); +} + +InputStreamer::~InputStreamer() +{ + running = false; + wait(); +} + +#include +void InputStreamer::run() +{ + lrtp_status_t status; + + lrtp = lrtp_init(&status, KEY, SSRC); + if(status != LRTP_OK) printf("O:lrtp_init err: %d\n", status); + + int res; + res = lrtp_create_profile(lrtp, PROFILE_JPEG, CSRC_V, OPTION_END); + if(res != 0) printf("O:lrtp_create_profile (v) err: %d\n", res); + + res = lrtp_create_profile(lrtp, PROFILE_OPUS, CSRC_A, OPTION_END); + if(res != 0) printf("O:lrtp_create_profile (a) err: %d\n", res); + + char packet[16*1024]; + while(running) { + qint64 packetsize = + socket.readDatagram(packet, (quint64)sizeof(packet), 0,0); + if(packetsize < 1) { + usleep(1000); + continue; + } + total += packetsize; + + // Now decode the sucker.... + lrtp_unpack(lrtp, packet, packetsize); + int n = 0; + int ret; + char frame[512 * 1024]; // 512kbyte should be enough for even the larges + // JPEG frames... + unsigned int csrc; + unsigned int ts; + while((ret = lrtp_dequeue_frame(lrtp, frame, sizeof(frame), &csrc, &ts)) + != 0) { + if(ret < 0) printf("\nlrtp_dequeue_frame: %d\n", ret); + if(csrc == CSRC_V) { + // Video frame + Frame f(frame, ret); + f.ts = ts; + emit newImage(f); + printf("v"); fflush(stdout); + } else if(csrc == CSRC_A) { + // Audio frame + Frame f(frame, ret); + f.ts = ts; + emit newAudio(f); + printf("a"); fflush(stdout); + } else { + printf("Unknown stream: CSRC: %d\n", csrc); + } + } + } + + status = lrtp_destroy_profile(lrtp, CSRC_V); + if(status != LRTP_OK) printf("O:lrtp_destroy_profile (v) err: %d\n", status); + + status = lrtp_destroy_profile(lrtp, CSRC_A); + if(status != LRTP_OK) printf("O:lrtp_destroy_profile (a) err: %d\n", status); + + status = lrtp_close(lrtp); + if(status != LRTP_OK) printf("O:lrtp_close err: %d\n", status); +} + +size_t InputStreamer::getTotal() +{ + size_t t = total; + total = 0; + return t; +} diff --git a/src/inputstreamer.h b/src/inputstreamer.h new file mode 100644 index 0000000..168485e --- /dev/null +++ b/src/inputstreamer.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * inputstreamer.h + * + * Sat Sep 20 10:15:50 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __SIMPLERTP_INPUTSTREAMER_H__ +#define __SIMPLERTP_INPUTSTREAMER_H__ + +#include +#include +#include + +#include + +#include "frame.h" + +class InputStreamer : public QThread { +Q_OBJECT +public: + InputStreamer(QHostAddress addr, quint16 port); + ~InputStreamer(); + + void run(); + + size_t getTotal(); + +signals: + void newImage(Frame frame); + void newAudio(Frame frame); + +private: + struct lrtp_t *lrtp; + + QHostAddress addr; + quint16 port; + + QUdpSocket socket; + + size_t total; + + volatile bool running; +}; + +#endif/*__SIMPLERTP_INPUTSTREAMER_H__*/ diff --git a/src/mainwindow.cc b/src/mainwindow.cc index 4dcac58..3e31136 100644 --- a/src/mainwindow.cc +++ b/src/mainwindow.cc @@ -27,38 +27,85 @@ #include "mainwindow.h" #include +#include + #include -MainWindow::MainWindow(QString v4ldev, QString adev) - : v4l(v4ldev), ah(adev) +MainWindow::MainWindow(QString v4ldev, QString adev, + QHostAddress addr, quint16 port) + : v4l(v4ldev), aih(adev), ostreamer(addr, port), istreamer(addr, port) { - connect(&v4l, SIGNAL(newImage(QImage)), - this, SLOT(newImage(QImage))); + /* // Self view: + connect(&v4l, SIGNAL(newImage(Frame)), + this, SLOT(newImage(Frame))); + */ + connect(&istreamer, SIGNAL(newImage(Frame)), + this, SLOT(newImage(Frame))); + + connect(&istreamer, SIGNAL(newAudio(Frame)), + &aoh, SLOT(newAudio(Frame))); - connect(&ah, SIGNAL(newAudio(framelist_t)), + /* + connect(&aih, SIGNAL(newAudio(framelist_t)), this, SLOT(newAudio(framelist_t))); + */ + connect(&v4l, SIGNAL(newImage(Frame)), + &ostreamer, SLOT(newImage(Frame))); + + connect(&aih, SIGNAL(newAudio(framelist_t)), + &ostreamer, SLOT(newAudio(framelist_t))); + + connect(&status_timer, SIGNAL(timeout()), + this, SLOT(updateStatus())); + + status_timer.start(1000); setCentralWidget(new QLabel()); + + statusBar()->showMessage("Starting..."); } -void MainWindow::newImage(QImage img) +void MainWindow::updateStatus() { + size_t ob = ostreamer.getTotal() / 60 / 1024; + size_t ib = istreamer.getTotal() / 60 / 1024; + QString status = + "In: " + QString::number(ib) + "kb/s" + + " - " + + "Out: " + QString::number(ob) + "kb/s" + ; + statusBar()->showMessage(status); +} + +void MainWindow::newImage(Frame frame) +{ + QImage img; + bool res = img.loadFromData((const uchar*)frame.data, frame.size, "JPG"); + // printf("processImage() => %s\n", res?"true":"false"); if(img.isNull()) { printf("Invalid image\n"); return; } - printf("img->w: %d\n", img.width()); - printf("img->h: %d\n", img.height()); + // printf("img->w: %d\n", img.width()); + // printf("img->h: %d\n", img.height()) QLabel *l = (QLabel*)centralWidget(); QPixmap p; p.convertFromImage(img); l->setPixmap(p); - printf("Got one\n"); + + // printf("v"); fflush(stdout); } void MainWindow::newAudio(framelist_t list) { - printf("audio: %d frames\n", list.size()); + /* + framelist_t::iterator i = list.begin(); + while(i != list.end()) { + sp.playSamples(*i); + } + // printf("audio: %d frames\n", list.size()); + */ + // printf("a"); fflush(stdout); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 0ce1f4a..78bd50b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -29,22 +29,32 @@ #include #include +#include #include "v4l.h" -#include "audiohandler.h" +#include "audioinputhandler.h" +#include "audiooutputhandler.h" +#include "outputstreamer.h" +#include "inputstreamer.h" class MainWindow : public QMainWindow { Q_OBJECT public: - MainWindow(QString v4ldev, QString adev); + MainWindow(QString v4ldev, QString adev, QHostAddress addr, quint16 port); public slots: - void newImage(QImage img); + void newImage(Frame frame); void newAudio(framelist_t list); + void updateStatus(); private: V4L v4l; - AudioHandler ah; + AudioInputHandler aih; + AudioOutputHandler aoh; + OutputStreamer ostreamer; + InputStreamer istreamer; + + QTimer status_timer; }; #endif/*__SIMPLERTP_MAINWINDOW_H__*/ diff --git a/src/opusdecoder.cc b/src/opusdecoder.cc new file mode 100644 index 0000000..195c699 --- /dev/null +++ b/src/opusdecoder.cc @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * opusdecoder.cc + * + * Sat Sep 20 11:15:23 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "opusdecoder.h" + +OpusDecoder::OpusDecoder() +{ + int err; + decoder = opus_decoder_create(48000, 1, &err); + if(err) printf("opus_decoder_create: %d\n", err); +} + +OpusDecoder::~OpusDecoder() +{ + if(decoder) opus_decoder_destroy(decoder); +} + +int OpusDecoder::decode(Frame frame, char *pcm, size_t size) +{ + if(!decoder) { + printf("Missing opus decoder handle\n"); + return 0; + } + + int res = opus_decode(decoder, + (const unsigned char*)frame.data, frame.size, + (opus_int16*)pcm, size, 0); + if(res >= 0) return res * sizeof(short); + printf("opus_decode: %d\n", res); +} diff --git a/src/opusdecoder.h b/src/opusdecoder.h new file mode 100644 index 0000000..13ee09e --- /dev/null +++ b/src/opusdecoder.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * opusdecoder.h + * + * Sat Sep 20 11:15:23 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __SIMPLERTP_OPUSDECODER_H__ +#define __SIMPLERTP_OPUSDECODER_H__ + +#include + +#include "frame.h" + +// Frame size 10ms +#define OPUS_FRAME_SIZE 10 + +class OpusDecoder { +public: + OpusDecoder(); + ~OpusDecoder(); + + int decode(Frame frame, char *pcm, size_t size); + +private: + struct OpusDecoder *decoder; +}; + +#endif/*__SIMPLERTP_OPUSDECODER_H__*/ diff --git a/src/opusencoder.cc b/src/opusencoder.cc index 77ea0d1..b5c343a 100644 --- a/src/opusencoder.cc +++ b/src/opusencoder.cc @@ -27,98 +27,62 @@ #include "opusencoder.h" #include -#include #include -#include "samplecache.h" - #define OPUS_BITRATE 64000 -struct opus_encoder_private_t { - OpusEncoder *ce; - SampleCache *cache; -}; - OpusEncoder::OpusEncoder() + : cache(5760 * 10) { - prv = new struct opus_encoder_private_t; - - prv->cache = new SampleCache(5760 * 10); - int error = 0; - - samplerate_t srate = opus_samplerate(); - - prv->ce = - opus_encoder_create(srate, 1/*channels()*/, OPUS_APPLICATION_AUDIO, &error); - if(!prv->ce || error != OPUS_OK) { - printf("srate: %d, ch: %d, Error: %d\n", srate, 1/*channels()*/, error); - //throw InitException(); + encoder = + opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, &error); + if(!encoder || error != OPUS_OK) { + printf("opus_encoder_create: %d\n", error); } // 500 to 512000 - opus_encoder_ctl(prv->ce, OPUS_SET_BITRATE(OPUS_BITRATE/*bitrate()*/)); + opus_encoder_ctl(encoder, OPUS_SET_BITRATE(OPUS_BITRATE)); // [0-10] with 10 representing the highest complexity. - opus_encoder_ctl(prv->ce, OPUS_SET_COMPLEXITY(10)); - - /* - printf("Encoder(fs: %d, ch: %d, bitrate: %d)\n", srate, channels(), - bitrate()); - */ + opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(10)); } OpusEncoder::~OpusEncoder() { - if(prv) { - if(prv->ce) opus_encoder_destroy(prv->ce); - if(prv->cache) delete prv->cache; - delete prv; - } + if(encoder) opus_encoder_destroy(encoder); } unsigned int OpusEncoder::framesize() { - return opus_samplerate() / 1000 * OPUS_FRAME_SIZE; // 10ms audio data -} - -samplerate_t OpusEncoder::opus_samplerate() -{ - return 48000; + return 48000 / 1000 * OPUS_FRAME_SIZE; // 10ms audio data } framelist_t OpusEncoder::encode(const char *pcm, size_t size) { framelist_t list; - // printf("PCM size: %d\n", size); - - samplerate_t srate = opus_samplerate(); - - if(prv->ce == NULL) { + if(encoder == NULL) { //throw InitException(); - printf("prv->ce == NULL\n"); + printf("encoder == NULL\n"); } - size_t bytes_per_packet = ((OPUS_BITRATE/*bitrate()*/ *framesize())/srate+4)/8; + size_t bytes_per_packet = ((OPUS_BITRATE *framesize())/48000+4)/8; - prv->cache->addSamples((short*)pcm, size / sizeof(short)); + cache.addSamples((short*)pcm, size / sizeof(short)); //size_t p = 0; opus_int16 *samples = new opus_int16[framesize()]; - while(prv->cache->cacheSize() >= framesize()) { - size_t sz = prv->cache->getSamples(samples, framesize()); + while(cache.cacheSize() >= framesize()) { + size_t sz = cache.getSamples(samples, framesize()); Frame *frame = new Frame(bytes_per_packet); - int n = opus_encode(prv->ce, samples, sz / 1/*channels()*//*sizeof(short)*/, + int n = opus_encode(encoder, samples, sz / 1, (unsigned char *)frame->data, (opus_int32)frame->size); - //printf("Compressed to %d\n", n); - if(n < 0) { printf("n < 0\n"); - //throw EncodingException(); } frame->size = n; @@ -127,7 +91,5 @@ framelist_t OpusEncoder::encode(const char *pcm, size_t size) } delete[] samples; - //printf("%d frames in list\n", list.size()); - return list; } diff --git a/src/opusencoder.h b/src/opusencoder.h index b0e16fd..9aeaf3a 100644 --- a/src/opusencoder.h +++ b/src/opusencoder.h @@ -27,13 +27,14 @@ #ifndef __SIMPLERTP_OPUS_ENCODER_H__ #define __SIMPLERTP_OPUS_ENCODER_H__ +#include + #include "frame.h" +#include "samplecache.h" // Frame size 10ms #define OPUS_FRAME_SIZE 10 -struct opus_encoder_private_t; - class OpusEncoder { public: OpusEncoder(); @@ -41,11 +42,11 @@ public: framelist_t encode(const char *pcm, size_t size); - samplerate_t opus_samplerate(); unsigned int framesize(); private: - struct opus_encoder_private_t *prv; + struct OpusEncoder *encoder; + SampleCache cache; }; #endif/*__SIMPLERTP_OPUS_ENCODER_H__*/ diff --git a/src/outputstreamer.cc b/src/outputstreamer.cc new file mode 100644 index 0000000..e652c7e --- /dev/null +++ b/src/outputstreamer.cc @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * streamer.cc + * + * Fri Sep 19 21:05:08 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "outputstreamer.h" + +#define KEY "123456789012345678901234567890123456789012345678901234567890" +#define SSRC 1234567890 +#define CSRC_V 42 +#define CSRC_A 43 + +OutputStreamer::OutputStreamer(QHostAddress addr, quint16 port) + : socket(this) +{ + total = 0; + + lrtp_status_t status; + + lrtp = lrtp_init(&status, KEY, SSRC); + if(status != LRTP_OK) printf("O:lrtp_init err: %d\n", status); + + int res; + res = lrtp_create_profile(lrtp, PROFILE_JPEG, CSRC_V, OPTION_END); + if(res != 0) printf("O:lrtp_create_profile (v) err: %d\n", res); + + res = lrtp_create_profile(lrtp, PROFILE_OPUS, CSRC_A, OPTION_END); + if(res != 0) printf("O:lrtp_create_profile (a) err: %d\n", res); + + this->addr = addr; + this->port = port; +} + +OutputStreamer::~OutputStreamer() +{ + lrtp_status_t status; + + status = lrtp_destroy_profile(lrtp, CSRC_V); + if(status != LRTP_OK) printf("O:lrtp_destroy_profile (v) err: %d\n", status); + + status = lrtp_destroy_profile(lrtp, CSRC_A); + if(status != LRTP_OK) printf("O:lrtp_destroy_profile (a) err: %d\n", status); + + status = lrtp_close(lrtp); + if(status != LRTP_OK) printf("O:lrtp_close err: %d\n", status); +} + +void OutputStreamer::newImage(Frame frame) +{ + int ret = 0; + ret = lrtp_enqueue_frame(lrtp, CSRC_V, + frame.data, frame.size, frame.ts, + LRTP_COPY); + if(ret != 0) printf("O:lrtp_enqueue_frame (v) err: %d\n", ret); + + char packet[16*1024]; + while( (ret = lrtp_pack(lrtp, packet, sizeof(packet))) > 0) { + //printf("v"); fflush(stdout); + sendPackage(packet, ret); + } + //CPPUNIT_ASSERT_EQUAL(ret, 0); + + free(frame.data); + // delete frame; + //free(frame); +} + +void OutputStreamer::newAudio(framelist_t frames) +{ + framelist_t::iterator fi = frames.begin(); + while(fi != frames.end()) { + Frame *frame = *fi; + + int ret = 0; + ret = lrtp_enqueue_frame(lrtp, CSRC_A, + frame->data, frame->size, frame->ts, + LRTP_COPY); + if(ret != 0) printf("O:lrtp_enqueue_frame (a) err: %d\n", ret); + + char packet[16*1024]; + while( (ret = lrtp_pack(lrtp, packet, sizeof(packet))) > 0) { + //printf("a"); fflush(stdout); + sendPackage(packet, ret); + } + //CPPUNIT_ASSERT_EQUAL(ret, 0); + + free(frame->data); + delete frame; + //free(frame); + + fi++; + } +} + +void OutputStreamer::sendPackage(const char *data, size_t size) +{ + total += size; + socket.writeDatagram(data, (quint64)size, addr, port); +} + +size_t OutputStreamer::getTotal() +{ + size_t t = total; + total = 0; + return t; +} diff --git a/src/outputstreamer.h b/src/outputstreamer.h new file mode 100644 index 0000000..f53d35a --- /dev/null +++ b/src/outputstreamer.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * outputstreamer.h + * + * Fri Sep 19 21:05:07 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of SimpleRTP. + * + * SimpleRTP 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 of the License, or + * (at your option) any later version. + * + * SimpleRTP 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 SimpleRTP; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __SIMPLERTP_STREAMER_H__ +#define __SIMPLERTP_STREAMER_H__ + +#include +#include + +#include + +#include "frame.h" + +class OutputStreamer : public QObject { +Q_OBJECT +public: + OutputStreamer(QHostAddress addr, quint16 port); + ~OutputStreamer(); + + size_t getTotal(); + +public slots: + void newImage(Frame frame); + void newAudio(framelist_t frames); + +private: + void sendPackage(const char *data, size_t size); + + struct lrtp_t *lrtp; + + QHostAddress addr; + quint16 port; + + QUdpSocket socket; + + size_t total; +}; + +#endif/*__SIMPLERTP_STREAMER_H__*/ diff --git a/src/simplertp.cc b/src/simplertp.cc index 837bec5..eea3ebd 100644 --- a/src/simplertp.cc +++ b/src/simplertp.cc @@ -34,11 +34,13 @@ int main(int argc, char *argv[]) QString v4ldev = "/dev/video0"; QString adev = "hw:2,0"; + QHostAddress addr("192.168.0.10"); + quint16 port = 10000; if(argc > 1) v4ldev = argv[1]; if(argc > 2) adev = argv[2]; - MainWindow wnd(v4ldev, adev); + MainWindow wnd(v4ldev, adev, addr, port); wnd.show(); return app.exec(); diff --git a/src/soundplayer.cc b/src/soundplayer.cc new file mode 100644 index 0000000..6aea2bf --- /dev/null +++ b/src/soundplayer.cc @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * soundplayer.cc + * + * Fri Sep 5 17:31:28 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Kaiman. + * + * Kaiman 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 of the License, or + * (at your option) any later version. + * + * Kaiman 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 Kaiman; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "soundplayer.h" + +#include +#include + +#define BUFSZ 512 + +SoundPlayer::SoundPlayer() +{ + start(); +} + +SoundPlayer::~SoundPlayer() +{ + running = false; + wait(); +} + +void SoundPlayer::run() +{ + + printf("SoundPlayer running\n"); + + ao_initialize(); + + ao_sample_format sf; + memset(&sf, 0, sizeof(sf)); + sf.bits = 16; + sf.rate = 16000; + sf.channels = 1; + sf.byte_format = AO_FMT_NATIVE; + + ao_device *dev = ao_open_live(ao_default_driver_id(), &sf, 0); + + running = true; + + short s[BUFSZ]; + while(running) { + sem.acquire(); + + char *pcm; + size_t size; + + { + QMutexLocker lock(&mutex); + pcm = qi.pcm; + size = qi.size; + } + + printf("."); fflush(stdout); + + ao_play(dev, pcm, size); + + //free(pcm); + + /* + memset(s, 0, BUFSZ * sizeof(short)); + + QList::iterator it = active.begin(); + while(it != active.end()) { + QueueItem &item = *it; + + for(size_t i = 0; i < BUFSZ; i++) { + if(item.pos >= item.size) { + free(item.samples); + it = active.erase(it); + goto nextitem; + } + s[i] += item.samples[item.pos]; + item.pos++; + } + + it++; + + nextitem: + int a = 1;(void)a; + } + + ao_play(dev, (char*)s, BUFSZ * sizeof(short)); + */ + } + + ao_close(dev); + ao_shutdown(); +} + +void SoundPlayer::playSamples(const char *pcm, size_t size) +{ + QMutexLocker lock(&mutex); + + qi.pcm = (char*)malloc(size); + memcpy(qi.pcm, pcm, size); + qi.size = size; + + sem.release(); +} diff --git a/src/soundplayer.h b/src/soundplayer.h new file mode 100644 index 0000000..28c0cf8 --- /dev/null +++ b/src/soundplayer.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * soundplayer.h + * + * Fri Sep 5 17:31:27 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Kaiman. + * + * Kaiman 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 of the License, or + * (at your option) any later version. + * + * Kaiman 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 Kaiman; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __KAIMAN_SOUNDPLAYER_H__ +#define __KAIMAN_SOUNDPLAYER_H__ + +#include +#include +#include +#include +#include + +#include "frame.h" + +class QueueItem { +public: + char *pcm; + size_t size; +}; + +class SoundPlayer : public QThread { +public: + SoundPlayer(); + ~SoundPlayer(); + + void playSamples(const char *pcm, size_t size); + + void run(); + +private: + volatile bool running; + QMutex mutex; + QSemaphore sem; + + QueueItem qi; + + QList queue; + QList active; +}; + +#endif/*__KAIMAN_SOUNDPLAYER_H__*/ diff --git a/src/v4l.cc b/src/v4l.cc index 38f53b7..55b0397 100644 --- a/src/v4l.cc +++ b/src/v4l.cc @@ -44,6 +44,8 @@ #include +#include + extern "C" { #include } @@ -791,6 +793,8 @@ int main(int argc, char **argv) V4L::V4L(QString device) { + qRegisterMetaType("Frame"); + dev_name = strdup(device.toStdString().c_str()); v4l = this; // Set global V4L object pointer. @@ -823,8 +827,12 @@ void V4L::run() void V4L::processImage(const void *p, int size) { + /* QImage img; bool res = img.loadFromData((const uchar *)p, size, "JPG"); - printf("processImage() => %s\n", res?"true":"false"); + //printf("processImage() => %s\n", res?"true":"false"); if(res) emit newImage(img); + */ + Frame frame((const char *)p, size); + emit newImage(frame); } diff --git a/src/v4l.h b/src/v4l.h index 2fe474e..7d381f0 100644 --- a/src/v4l.h +++ b/src/v4l.h @@ -28,9 +28,10 @@ #define __SIMPLERTP_V4L_H__ #include -#include #include +#include "frame.h" + class V4L : public QThread { Q_OBJECT public: @@ -42,7 +43,7 @@ public: void processImage(const void *p, int size); signals: - void newImage(QImage image); + void newImage(Frame frame); }; #endif/*__SIMPLERTP_V4L_H__*/ -- cgit v1.2.3