C++ RTP-Empfänger - welches Speichermanagement ?

C

Crymes

Guest
C++ RTP-Empfänger - welches Speichermanagement ?

Hallo,
ich möchte in C++ eine Empfängerklasse für die RTP Übertragung von Bildern erstellen. Folgendes habe ich schon (noch unvollständig) :

Code:
#include "Capturable.h"
#include "Receivable.h"
#include <memory>
#include <cstdint>
#include <vector>


#define RTPRECEIVER_STANDARD_PICTURE_SIZE 5000000
#define RTPRECEIVER_STANDARD_PACKET_SIZE 1500
#define RTPRECEIVER_STANDARD_PICTURES_IN_FLIGHT 3


class RTPReceiver : public Receivable
{
public:
	RTPReceiver(Capturable&, unsigned int = RTPRECEIVER_STANDARD_PICTURE_SIZE, unsigned int = RTPRECEIVER_STANDARD_PACKET_SIZE, unsigned int = RTPRECEIVER_STANDARD_PICTURES_IN_FLIGHT);
	virtual ~RTPReceiver();
	void receiveData(const char*, std::size_t);


protected:
	class Fragment
	{
	public:
		Fragment(unsigned int maxsize);
		virtual ~Fragment();
		void storeData(char* data, unsigned int size, uint16_t sequencenumber);
		uint16_t getSequenceNumber();
		unsigned int getDataSize();
		char* getData();
		unsigned int getMaxSize();


	protected:
		unsigned int fragmentsize;
		uint16_t sn;
		unsigned int datasize;
		std::unique_ptr<char[]> fragmentdata;
	};
	class Picture
	{
	public:
		Picture(unsigned int maxsize);
		virtual ~Picture();
		void releaseFragments(std::vector<Fragment> freefragments);
		void addFragment(Fragment *fragment, bool isstartfragment, bool isendfragment);
		bool isComplete();
		unsigned int getSizeOfPicture();
		void storePicture(char*data);


	protected:
		uint32_t timestamp;
		bool hasstartfragment;
		bool hasendfragment;
		std::vector<Fragment> fragments;
	};
	Capturable &C;
	std::unique_ptr<char[]> picturebuffer;
};

Die Klasse Fragment soll ein Stückchen eines Bildes beschreiben und die Klasse Picture bekommt nach und nach mehr Fragmente bis das Bild komplett ist.

Folgendes habe ich mir nun überlegt:

- Jedes Mal beim Eintreffen eines UDP Paketes mit new ein neues Fragment zu erstellen ist zu Fehleranfällig
--> Beste Lösung wäre das placement-new, da die Fragmente unterschiedlich groß sein können (aber schwierig zu implementieren)
--> Als Alternative am Anfang in einem Vector ca. 10000 Fragmente auf Vorrat erstellen
--> 3 Vektoren: AvailableFragmeents (unbenutzte Fragmente), AvailablePictures (unbenutzte Bilder, ca. 3 Bilder am Anfang), ReceivedPictures (Bilder unter konstruktion)

Somit hätte ich noch einen Vektor in Picture der je nach Anzahl der Fragmente wächst
Jetzt stellt sich die Frage ob ich die Fragmente zwischen den Vektoren hin und her kopiere oder ob ich stattdessen Pointer nehme und was passiert wenn pushBack() fehlschlägt (memleak?)

Oder würdet ihr eine komplett andere Speicherverwaltung nehmen ?
 
AW: C++ RTP-Empfänger - welches Speichermanagement ?

So hab mich nun für ein Speichermanagement entschieden:
Fragmente werden mit new angelegt und nahc benutzen in einem vector bestehend aus unique_pointern zu Fragmenten gepuffert.
Die Bilder werden in einem Vector gespeichert und dort mithilfe von der emplace-Methode erstellt.

Jetzt habe ich aber ein paar komische Fehler die glaube ich mit der move/copy Semantik zu tun haben.
Das ist mein Header:
Code:
#ifndef RTPRECEIVER_H_
#define RTPRECEIVER_H_


#include "Capturable.h"
#include "Receivable.h"
#include <memory>
#include <cstdint>
#include <vector>


#define RTPRECEIVER_STANDARD_PICTURE_SIZE 5000000
#define RTPRECEIVER_STANDARD_PICTURES_IN_FLIGHT 3


class RTPReceiver : public Receivable
{
public:
    RTPReceiver(Capturable&, unsigned int = RTPRECEIVER_STANDARD_PICTURE_SIZE, unsigned int = RTPRECEIVER_STANDARD_PICTURES_IN_FLIGHT);
    virtual ~RTPReceiver();
    void receiveData(const char*, std::size_t);


protected:
    class Fragment
    {
    public:
        Fragment(unsigned int);
        virtual ~Fragment();
        void storeData(const char*, unsigned int, uint16_t, bool, bool);
        uint16_t getSequenceNumber();
        bool isStartFragment();
        bool isEndFragment();
        unsigned int getDataSize();
        char* getData();
        unsigned int getMaxSize();


    protected:
        unsigned int fragmentsize;
        uint16_t sn;
        bool isstartfragment;
        bool isendfragment;
        unsigned int datasize;
        std::unique_ptr<char[]> fragmentdata;
    };
    class Picture
    {
    public:
        Picture(uint32_t timestamp, std::vector<std::unique_ptr<Fragment>>&);
        virtual ~Picture();
        void addFragment(std::unique_ptr<Fragment>);
        uint32_t getTimestamp();
        bool isComplete();
        unsigned int getSizeOfPicture();
        void storePicture(char*);


    protected:
        uint32_t timestamp;
        std::vector<std::unique_ptr<Fragment>> Fragments;
        std::vector<std::unique_ptr<Fragment>> &Recyclingvec;
    };
    Capturable &C;
    unsigned int maxpicturesize;
    unsigned int maxpicturesinflight;
    std::vector<std::unique_ptr<Fragment>> Availablefragments;
    std::vector<Picture> Pictures;
    std::unique_ptr<char[]> picturebuffer;
};


#endif
Folgende Zeilen meiner Implementierung werden vom Compiler blau angemalt:
Code:
Pictures.erase(Pictures.begin() + i); //i ist unsigned int
Pictures.emplace_back(timestamp, Availablefragments); //timestamp ist eine uint32_t

Und der Fehler vom Compiler lautet:
Code:
18:09:01 **** Incremental Build of configuration Release for project RTPTest ****
make all 
Building file: ../src/RTPReceiver.cpp
Invoking: GCC C++ Compiler
g++ -std=c++1y -O2 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/RTPReceiver.d" -MT"src/RTPReceiver.o" -o "src/RTPReceiver.o" "../src/RTPReceiver.cpp"
In file included from /usr/include/c++/5/memory:62:0,
                 from ../src/RTPReceiver.h:6,
                 from ../src/RTPReceiver.cpp:1:
/usr/include/c++/5/bits/stl_algobase.h: In instantiation of ‘static _OI std::__copy_move<true, false, std::random_access_iterator_tag>::__copy_m(_II, _II, _OI) [with _II = RTPReceiver::Picture*; _OI = RTPReceiver::Picture*]’:
/usr/include/c++/5/bits/stl_algobase.h:402:44:   required from ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = true; _II = RTPReceiver::Picture*; _OI = RTPReceiver::Picture*]’
/usr/include/c++/5/bits/stl_algobase.h:438:45:   required from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = true; _II = __gnu_cxx::__normal_iterator<RTPReceiver::Picture*, std::vector<RTPReceiver::Picture> >; _OI = __gnu_cxx::__normal_iterator<RTPReceiver::Picture*, std::vector<RTPReceiver::Picture> >]’
/usr/include/c++/5/bits/stl_algobase.h:503:39:   required from ‘_OI std::move(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<RTPReceiver::Picture*, std::vector<RTPReceiver::Picture> >; _OI = __gnu_cxx::__normal_iterator<RTPReceiver::Picture*, std::vector<RTPReceiver::Picture> >]’
/usr/include/c++/5/bits/vector.tcc:145:2:   required from ‘std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::_M_erase(std::vector<_Tp, _Alloc>::iterator) [with _Tp = RTPReceiver::Picture; _Alloc = std::allocator<RTPReceiver::Picture>; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<RTPReceiver::Picture*, std::vector<RTPReceiver::Picture> >; typename std::_Vector_base<_Tp, _Alloc>::pointer = RTPReceiver::Picture*]’
/usr/include/c++/5/bits/stl_vector.h:1147:24:   required from ‘std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::erase(std::vector<_Tp, _Alloc>::const_iterator) [with _Tp = RTPReceiver::Picture; _Alloc = std::allocator<RTPReceiver::Picture>; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<RTPReceiver::Picture*, std::vector<RTPReceiver::Picture> >; typename std::_Vector_base<_Tp, _Alloc>::pointer = RTPReceiver::Picture*; std::vector<_Tp, _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const RTPReceiver::Picture*, std::vector<RTPReceiver::Picture> >; typename __gnu_cxx::__alloc_traits<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type>::const_pointer = const RTPReceiver::Picture*]’
../src/RTPReceiver.cpp:97:41:   required from here
/usr/include/c++/5/bits/stl_algobase.h:359:18: error: use of deleted function ‘RTPReceiver::Picture& RTPReceiver::Picture::operator=(const RTPReceiver::Picture&)’
        *__result = std::move(*__first);
                  ^
In file included from ../src/RTPReceiver.cpp:1:0:
../src/RTPReceiver.h:42:8: note: ‘RTPReceiver::Picture& RTPReceiver::Picture::operator=(const RTPReceiver::Picture&)’ is implicitly deleted because the default definition would be ill-formed:
  class Picture
        ^
../src/RTPReceiver.h:42:8: error: non-static reference member ‘std::vector<std::unique_ptr<RTPReceiver::Fragment> >& RTPReceiver::Picture::Recyclingvec’, can’t use default assignment operator
In file included from /usr/include/c++/5/memory:64:0,
                 from ../src/RTPReceiver.h:6,
                 from ../src/RTPReceiver.cpp:1:
/usr/include/c++/5/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::unique_ptr<RTPReceiver::Fragment>; _Args = {const std::unique_ptr<RTPReceiver::Fragment, std::default_delete<RTPReceiver::Fragment> >&}]’:
/usr/include/c++/5/bits/stl_uninitialized.h:75:18:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<RTPReceiver::Fragment>*, std::vector<std::unique_ptr<RTPReceiver::Fragment> > >; _ForwardIterator = std::unique_ptr<RTPReceiver::Fragment>*; bool _TrivialValueTypes = false]’
/usr/include/c++/5/bits/stl_uninitialized.h:126:15:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<RTPReceiver::Fragment>*, std::vector<std::unique_ptr<RTPReceiver::Fragment> > >; _ForwardIterator = std::unique_ptr<RTPReceiver::Fragment>*]’
/usr/include/c++/5/bits/stl_uninitialized.h:281:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<RTPReceiver::Fragment>*, std::vector<std::unique_ptr<RTPReceiver::Fragment> > >; _ForwardIterator = std::unique_ptr<RTPReceiver::Fragment>*; _Tp = std::unique_ptr<RTPReceiver::Fragment>]’
/usr/include/c++/5/bits/stl_vector.h:322:31:   required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = std::unique_ptr<RTPReceiver::Fragment>; _Alloc = std::allocator<std::unique_ptr<RTPReceiver::Fragment> >]’
../src/RTPReceiver.h:42:8:   [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/c++/5/bits/stl_uninitialized.h:126:15:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = RTPReceiver::Picture*; _ForwardIterator = RTPReceiver::Picture*]’
/usr/include/c++/5/bits/stl_uninitialized.h:281:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = RTPReceiver::Picture*; _ForwardIterator = RTPReceiver::Picture*; _Tp = RTPReceiver::Picture]’
/usr/include/c++/5/bits/stl_uninitialized.h:303:2:   required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = RTPReceiver::Picture*; _ForwardIterator = RTPReceiver::Picture*; _Allocator = std::allocator<RTPReceiver::Picture>]’
/usr/include/c++/5/bits/vector.tcc:422:8:   required from ‘void std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) [with _Args = {unsigned int&, std::vector<std::unique_ptr<RTPReceiver::Fragment, std::default_delete<RTPReceiver::Fragment> >, std::allocator<std::unique_ptr<RTPReceiver::Fragment, std::default_delete<RTPReceiver::Fragment> > > >&}; _Tp = RTPReceiver::Picture; _Alloc = std::allocator<RTPReceiver::Picture>]’
/usr/include/c++/5/bits/vector.tcc:101:23:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {unsigned int&, std::vector<std::unique_ptr<RTPReceiver::Fragment, std::default_delete<RTPReceiver::Fragment> >, std::allocator<std::unique_ptr<RTPReceiver::Fragment, std::default_delete<RTPReceiver::Fragment> > > >&}; _Tp = RTPReceiver::Picture; _Alloc = std::allocator<RTPReceiver::Picture>]’
../src/RTPReceiver.cpp:118:55:   required from here
/usr/include/c++/5/bits/stl_construct.h:75:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = RTPReceiver::Fragment; _Dp = std::default_delete<RTPReceiver::Fragment>]’
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^
In file included from /usr/include/c++/5/memory:81:0,
                 from ../src/RTPReceiver.h:6,
                 from ../src/RTPReceiver.cpp:1:
/usr/include/c++/5/bits/unique_ptr.h:356:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^
make: *** [src/RTPReceiver.o] Fehler 1
src/subdir.mk:34: die Regel für Ziel „src/RTPReceiver.o“ scheiterte


18:09:02 Build Finished (took 1s.437ms)

Weiß jemand woran das liegt ?
 
AW: C++ RTP-Empfänger - welches Speichermanagement ?

Die Klasse Picture ist nicht kopierbar. In dem Fall hier ist der copy assignment operator Picture& operator=(const Picture& o) als gelöscht markiert, da mindestens eine Membervariable nicht kopierbar ist. Das dürften die unique_ptr Vektoren sein. Eventuell reicht es schon, den passenden move assignment operator zu ergänzen:
Code:
Picture& operator=(Picture&& o) = default;
 
AW: C++ RTP-Empfänger - welches Speichermanagement ?

Ich habe im Destruktor von Picture Code stehen der die Fragmente in meinen Availablefragments Vektor von RTPReceiver moved. Nach diesem Artikel Move assignment operator - cppreference.com gibt es deshalb keinen Default move "=" Operator.
Werds nachher mal probieren obs mit deinem Tipp klappt, Bingo88, aber denk mal dass du Recht hast !
 
AW: C++ RTP-Empfänger - welches Speichermanagement ?

So hab den Fehler gefunden.
- Ich musste noch den default move-Konstruktor und den default move-Opoerator hinzufügen
- ein Vektor kann keine Klassen aufnehmen die Referenzen enthalten. Hab die einfach gegen einen Pointer getauscht. Als Parameter im Konstruktor bekomme ich trotzdem eine Referenz, macht also nach außen keinen Unterschied.

Gibts eigentlich einen bekannten Algorithmus der die Reihenfolge der Sequenznummern wiederherstellt?
Mein Algorithmus funktioniert aber wenn's zu viele Fragmente pro Bild werden ist er zu langsam :schief:
 
AW: C++ RTP-Empfänger - welches Speichermanagement ?

Ad hoc fällt mir da nur eine sortierte Liste bzw. ein BST/B-Baum ein.
 
Zurück