Parsen eines Paketes



  • Hi an alle,

    ich hab gerade ein kleines Problem. Ich muss ein SDP Paket parsen, ich will die Parameter der einzelnen Zeilen wegschreiben.
    Jetzt habe ich für das Paket selbst eine Struktur aufgebaut.

    struct sdpLine
    {
     string HeaderName;
     string ParamName;
     string ParamValue;
    }
    

    Weggeschrieben wird jede Zeile in einen vector<sdpLine>
    Nun zum Problem.
    Es gibt im SD Protokoll die Mediazeile und dieser sind mehrere Attribute als einzelnen Zeilen zugeordnet.

    //Sieht dann ungefähr so aus.
    m= 45678 RTP/AVP 0 3 8 101
    //und die dazugehörenden a - Attribute
    a= rtpmap: 0 ULAW/8000
    a= rtpmap: 3 GSM/8000
    a= rtpmap: 8 ALAW/8000
    a= rtpmap: 101 telephone-event/8000
    a= fmtp: 101 0-16
    a= ptime: 20
    a= sendrecv
    

    Wie kann ich diese denn nun am besten so wegschreiben, dass ich den Bezug zum MediaTag nicht verliere?

    Gruß und einen schönen Abend.



  • .. teilst Du uns noch mit, welche Info in welche Variable soll. Das ist nicht offensichtlich.
    Geraten:
    1.)
    HeaderName = "45678 RTP/AVP 0 3 8 101"
    ParamName = "rtpmap"
    ParamValue = "0 ULAW/8000"
    2.)
    HeaderName = "45678 RTP/AVP 0 3 8 101" // nochmal das gleiche wie oben?
    ParamName = "rtpmap"
    ParamValue = "3 GSM/8000"

    oder anders?



  • Sorry, ja klar teile ich das noch mit:D

    Also die Variablennamen sind etwas blöd gewählt.

    1.)
    Headername = "m"
    ParamName = "PortNo"
    ParamValue = "45678"

    2.)
    HeaderName = "m"
    ParamName = "protocol"
    ParamValue = "RTP/AVP"

    so hab ich mir das gedacht.
    Und die Ziffern 0, 3, 8, 101 stehen für verwendete Codecs
    Die a - Attribute geben z.B. eine genauere Beschreibung des Codecs an.
    Darum die gewünschte Zuordnung zum m - Tag.
    Aber auch, da sich mehrere m - Tags in einem Paket befinden können.
    Die wieder eigene a - Attribute haben.



  • mr. main schrieb:

    1.)
    Headername = "m"
    ParamName = "PortNo" // <-- ????
    ParamValue = "45678"

    2.)
    HeaderName = "m"
    ParamName = "protocol" // <-- ????
    ParamValue = "RTP/AVP"

    😕 woher kommen die 'ParamName'? Sind die fest mit dem 'm' verdrahtet?



  • Die Parameternamen ergeben sich aus dem Aufbau des media(m) - Tags im SDP nach RFC 4566.



  • lt. RFC 4566 ist:

    5.14. Media Descriptions ("m=")
          m=<media> <port> <proto> <fmt> ...
    

    ist das <media> optional oder fehlt es bei Dir? und wie sieht ein Beispiel mit 'a=' aus?



  • Oh...ok das <media> fehlt bei mir. Dabei kann es sich um die Werte: Audio, Video, Text, Application oder message handeln.

    Daher auch die Möglichkeit das mehrere Media Tags im SDP sein können.

    Die (a)ttribute können unterschiedlich sein.

    a= rtpmap: 0 ULAW/8000 //für codecs
    a= rtpmap: 101 telephone-event/8000
    a= fmtp: 101 0-16 //Bei DTMF für die zu erkennenden Ziffern
    a= sendrecv //soll die MediaSession empfangen und senden oder nur empfangen oder nur senden oder ist sie inactive?
    


  • .. also das wird richtig schwierig, auch weil die 'a='s vom Inhalt der 'm='s abhängen. Ich meld' mich später - voraussichtlich bis morgen abend noch mal.



  • Ja schwierig wird´s sicher...hab selbst schon vieles durchdacht will aber alles nicht so recht passen:).
    Danke für die Mühe und Hilfe... 👍



  • In der Beschreibung des SDP-Formats ist eine Grammatik im BNFormat angegeben. Das ruft natürlich förmlich nach boost.spirit. Ich habe hier noch die klassische Spirit Version benutzt, da ich mich damit besser auskenne.

    Die Kommentare habe ich direkt aus der Format-Beschreibung kopiert und dann in das boost.spirit.BNF 'übersetzt' (s. struct SdpParser::definition Konstruktor):

    #include <fstream>
    #include <iostream>
    #include <iterator> // ostream_iterator
    #include <memory> // auto_ptr<>
    #include <sstream> // ostringstream
    #include <string>
    #include <vector>
    
    #pragma warning( disable : 4180 ) // warning C4180: Auf Funktionstyp angewendeter Qualifizierer ist ohne Bedeutung; wird ignoriert
    #include <boost/bind.hpp>
    //#define BOOST_SPIRIT_DEBUG  // bei Parserfehler aktivieren (s. <http://www.boost.org/doc/libs/1_42_0/libs/spirit/classic/doc/debugging.html>)
    #include <boost/spirit/include/classic.hpp>
    namespace spirit = boost::spirit::classic;
    
    struct sdpLine
    {
        sdpLine( const std::string& header, const std::string& paramName, const std::string& paramValue )
            : HeaderName( header )
            , ParamName( paramName )
            , ParamValue( paramValue )
        {}
        std::string HeaderName;
        std::string ParamName;
        std::string ParamValue;
    }; 
    std::ostream& operator<<( std::ostream& out, const sdpLine& x )
    {
        out << x.HeaderName << "=" << x.ParamName;
        if( !x.ParamValue.empty() )
            out << ": " << x.ParamValue;
        return out;
    }
    
    #define ADD( h, n ) boost::bind( &SdpParser::add2, &self, (h), (n), _1, _2 )
    #define ADD_M( n ) ADD( "m", n )
    #define ATT_FIELD boost::bind( &SdpParser::SetParamName, &self, _1, _2 )
    #define ATT_FIELD0 boost::bind( &SdpParser::add1, &self, "a", _1, _2 )
    #define ATT_VALUE boost::bind( &SdpParser::add2, &self, "a", boost::bind( &SdpParser::ParamName, &self ), _1, _2 )
    
    template< typename I >
    class SdpParser : public spirit::grammar< SdpParser< I > >
    {
    public:
        SdpParser( std::vector< sdpLine >& storage )
            : m_storage( storage )
            , m_paramName( new std::string )
        {}
        void add1( const std::string& header, I first, I last ) const
        {
            m_storage.push_back( sdpLine( header, std::string( first, last ), std::string() ) );
        }
        void add2( const std::string& header, const std::string& paramName, I first, I last ) const
        {
            m_storage.push_back( sdpLine( header, paramName, std::string( first, last ) ) );
        }
        void SetParamName( I first, I last ) const
        {
            *m_paramName = std::string( first, last );
        }
        std::string ParamName() const { return *m_paramName; }
    
        template< typename ScannerT >
        struct definition
        {
            definition( SdpParser const& self )
            {
                using namespace spirit;
                sdp_file = *(media_field >> *attribute_fields);
    
                // Quelle BNFormat: <http://tools.ietf.org/html/rfc4566#section-9>
                //  media-field =         %x6d "=" media SP port ["/" integer] SP proto 1*(SP fmt) CRLF
                media_field = chseq_p("m=") >> media[ADD_M("media")] >> SP >> (port >> !('/' >> digit_p))[ADD_M("port")] >> SP >> proto[ADD_M("proto")] >> +(SP >> fmt[ADD_M("fmt")]) >> eol_p;
    
                //  media =               token  ;typically "audio", "video", "text", or "application"
                media = chseq_p("audio") | chseq_p("video") | chseq_p("text") | chseq_p("application");
    
                //  port =                1*DIGIT
                port = +digit_p;
    
                //  proto  =              token *("/" token)   ;typically "RTP/AVP" or "udp"
                proto = token >> *('/' >> token);
    
                //  fmt =                 token  ;typically an RTP payload type for audio and video media
                fmt = token;
    
                //  attribute-fields =    *(%x61 "=" attribute CRLF)
                attribute_fields = chseq_p("a=") >> attribute >> eol_p;
    
                //  attribute = (att-field ":" att-value) / att-field
                attribute = (att_field[ATT_FIELD] >> ':' >> att_value[ATT_VALUE]) | att_field[ATT_FIELD0];
    
                //  att-field =           token
                att_field = token;
    
                //  att-value =           byte-string
                att_value = byte_string;
    
                // --   sonstiges
                token = +token_char;
                //  token_char =  %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
                token_char = ch_p(0x21) | range_p(0x23,0x27) | range_p(0x2A,0x2B) | range_p(0x2D,0x2E) | range_p(0x30,0x39) | range_p(0x41,0x5A) | range_p(0x5E,0x7E); 
                //  byte-string = 1*(%x01-09/%x0B-0C/%x0E-FF)   ;any byte except NUL, CR, or LF
                byte_string = +(anychar_p - ch_p(0) - eol_p);
                SP = ch_p(0x20);
            }
    
            spirit::rule< ScannerT > SP, token_char, byte_string, token, att_value, att_field, attribute, attribute_fields, fmt, port, proto, media, media_field, sdp_file;
            spirit::rule< ScannerT > const&
                start() const { return sdp_file; }
    
        };
    private:
        std::vector< sdpLine >& m_storage;
        std::auto_ptr< std::string > m_paramName;
    };
    
    std::string File2String( const char* filename )
    {
        std::ostringstream buf;
        std::ifstream file( filename );
        buf << file.rdbuf();
        return buf.str();
    }
    
    int main()
    {
        using namespace std;
        string in = File2String( "input.txt" );
        vector< sdpLine > result;
        SdpParser< string::const_iterator > sdpParser( result );
        if( parse( in.begin(), in.end(), sdpParser.use_parser< 0 >() ).full )
        {
            copy( result.begin(), result.end(), ostream_iterator< sdpLine >( cout << "Ergebnis:\n", "\n" ) );
            cout << endl;
        }
        else
            cout << "Parser Fehler" << endl;
        return 0;
    }
    

    Die Datei 'input.txt' enthält:

    m=audio 45678 RTP/AVP 0 3 8 101
    a=rtpmap: 0 ULAW/8000
    a=rtpmap: 3 GSM/8000
    a=rtpmap: 8 ALAW/8000
    a=rtpmap: 101 telephone-event/8000
    a=fmtp: 101 0-16
    a=ptime: 20
    a=sendrecv
    m=video 20001 RTP/AVP 31
    a=rtpmap:31 H261/90000
    

    und die Ausgabe des Programms ist:

    Ergebnis:
    m=media: audio
    m=port: 45678
    m=proto: RTP/AVP
    m=fmt: 0
    m=fmt: 3
    m=fmt: 8
    m=fmt: 101
    a=rtpmap:  0 ULAW/8000
    a=rtpmap:  3 GSM/8000
    a=rtpmap:  8 ALAW/8000
    a=rtpmap:  101 telephone-event/8000
    a=fmtp:  101 0-16
    a=ptime:  20
    a=sendrecv
    m=media: video
    m=port: 20001
    m=port: RTP/AVP
    m=fmt: 31
    a=rtpmap: 31 H261/90000
    

    falls Dir dass zu abgefahren sein sollte oder Du boost nicht benutzen willst/kannst, so gäbe es auch noch eine Lösung mit std::istream.

    Gruß
    Werner



  • Morgen Werner,

    leider hab ich von boost überhaupt keine Ahnung. Damit hab ich noch nie gearbeitet.
    Aber ich werd mal versuchen mich durch deinen Code zu wurschteln.
    Wenn ich fragen haben sollte lasse ich von mir hören(die werde ich sicher haben; 😃 ).

    Aber nochmals allerbesten Dank für deine Mühe!



  • Zu Spirit gibts eine umfangreiche online-Doku mit vielen Beispielen und hier im Magazin auch einen Artikel. Ist allerdings auf den ersten Blick etwas brainfucked, aber bem zweiten Lesen wirds eigentlich recht klar...



  • Danke @ pumuckl, werde mir den Artikel später mal durchlesen.



  • mr. main schrieb:

    Wie kann ich diese denn nun am besten so wegschreiben, dass ich den Bezug zum MediaTag nicht verliere?

    .. das hatte ich noch vergessen, und habe mir daraufhin noch mal die Definition von diesem SDP angeschaut. Das beinhaltet eine ganze Menge, aber Du scheinst ja letztlich an den Media-Descriptions interessiert zu sein. Mir ist aber nicht klar, was Du dann mit dieser struct sdpLine genau anfangen möchtest. Wenn man sich mehr an der Domain SDP orientiert, dann müßte das was Du benötigst doch etwa so aussehen.

    eine Media-Description

    struct MediaDescription
    {
        MediaField m_mediaField;
        std::vector< AttributeField::Ptr > m_attributeFields;
    };
    

    , die aus dem Media-Feld und den dazugehörigen Attributen besteht. Und von diesen Attributen gibt es verschieden Typen (rtpmap, sendrecv, u.a.), deshalb wähle ich hier einen Container (std::vector) mit Pointern auf polymorphe Objekte.
    D.h. Eine Media-Description hat genau ein Media-Feld (eine Zeile wie m=audio 45678 RTP/AVP 0 3 8 101) und alle Attributen in diesem Objekt gehören zu diesem Media-Feld.

    Dem Parser ist das egal, ich habe ihn nur dahingehend erweitert, dass er verschieden Typen von Attributen unterscheidet. In der hier vorgestellte Variante die Typen rtpmap (ein Attribut mit vielen Parametern) und sendrecv (ein Attribut ohne Parameter). Weitere Attributtypen lassen sich natürlich leicht hinzufügen.

    #include "sdp.h" // <-- s.u.
    #include <fstream>
    #include <iostream>
    #include <iterator> // ostream_iterator
    #include <sstream> // ostringstream
    #include <string>
    #include <vector>
    
    #include <boost/ref.hpp>
    //#define BOOST_SPIRIT_DEBUG  // bei Parserfehler aktivieren (s. <http://www.boost.org/doc/libs/1_42_0/libs/spirit/classic/doc/debugging.html>)
    #include <boost/spirit/include/classic.hpp>
    namespace spirit = boost::spirit::classic;
    
    #define FCTR( classname, body ) \
        struct classname  { \
            explicit classname( const SdpParser& prs ) : m_target( prs.m_storage ) {} \
            template< typename I > \
            void operator()( I first, I last ) const { \
                body; } \
            storage_type& m_target;  }
    
    #define FCTRV( classname, body ) \
        struct classname  { \
            explicit classname( const SdpParser& prs ) : m_target( prs.m_storage ) {} \
            template< typename T > \
            void operator()( T val ) const { \
                body; } \
            storage_type& m_target;  }
    
    class SdpParser : public spirit::grammar< SdpParser >
    {
    public:
        typedef std::vector< MediaDescription > storage_type;
        SdpParser( storage_type& storage )
            : m_storage( storage )
        {}
        // --   Funktoren zum Füllen der 'Nutzlast'
        FCTR( Start, m_target.push_back( MediaDescription() ) );
        FCTR( AddMedia, m_target.back().m_mediaField.m_media = std::string( first, last ) );
        FCTR( MediaPort, m_target.back().m_mediaField.m_port = std::string( first, last ) );
        FCTR( MediaProto, m_target.back().m_mediaField.m_protocol = std::string( first, last ) );
        FCTRV( MediaAddFmt, m_target.back().m_mediaField.m_fmts.push_back( val ) );
        FCTR( AttSendrecv, m_target.back().m_attributeFields.push_back( AttributeField::Ptr( new AttributeSendrecv ) ) );
        FCTR( AttUnknown, m_target.back().m_attributeFields.push_back( AttributeField::Ptr( new AttributeUnknown( first, last ) ) ) );
        FCTR( UnknownValue, dynamic_cast< AttributeUnknown& >( *m_target.back().m_attributeFields.back() ).m_paramValue = std::string( first, last ) );
        FCTR( AttRtpmap, m_target.back().m_attributeFields.push_back( AttributeField::Ptr( new AttributeRtpmap ) ) );
        FCTR( RtpmapPayload, dynamic_cast< AttributeRtpmap& >( *m_target.back().m_attributeFields.back() ).m_payloadType = std::string( first, last ) );
        FCTR( RtpmapEncoding, dynamic_cast< AttributeRtpmap& >( *m_target.back().m_attributeFields.back() ).m_encodingName = std::string( first, last ) );
        FCTRV( RtpmapClockrate, dynamic_cast< AttributeRtpmap& >( *m_target.back().m_attributeFields.back() ).m_clockrate = val );
    
        // --   Parser Definition
        template< typename ScannerT >
        struct definition
        {
            definition( SdpParser const& self )
            {
                using namespace spirit;
                // Quelle BNFormat: <http://tools.ietf.org/html/rfc4566#section-9>
                sdp_file = *media_description;
    
                //  media-descriptions =  *( media-field information-field *connection-field bandwidth-fields
                //                         key-field attribute-fields )
                media_description = media_field >> *skip_field >> *attribute_field;
    
                skip_field = (ch_p('i') | ch_p('c') | ch_p('b') | ch_p('k')) >> '=' >> byte_string >> eol_p;
    
                //  media-field =         %x6d "=" media SP port ["/" integer] SP proto 1*(SP fmt) CRLF
                media_field = chseq_p("m=")[Start(self)] >> media[AddMedia(self)] 
                    >> SP >> (port >> !('/' >> digit_p))[MediaPort(self)] >> SP >> proto[MediaProto(self)] >> +(SP >> uint_p/*fmt*/[MediaAddFmt(self)]) >> eol_p;
    
                //  media =               token  ;typically "audio", "video", "text", or "application"
                media = chseq_p("audio") | chseq_p("video") | chseq_p("text") | chseq_p("application");
    
                //  port =                1*DIGIT
                port = +digit_p;
    
                //  proto  =              token *("/" token)   ;typically "RTP/AVP" or "udp"
                proto = token >> *('/' >> token);
    
                //  fmt =                 token  ;typically an RTP payload type for audio and video media
                fmt = token;
    
                //  attribute-fields =    *(%x61 "=" attribute CRLF)
                attribute_field = chseq_p("a=") >> attribute >> eol_p;
    
                //  attribute = ..
                attribute = att_rtpmap | att_sendrecv | att_unknown;
                //  a=rtpmap:<payload type> <encoding name>/<clock rate> [ /<encoding parameters>]
                att_rtpmap = chseq_p("rtpmap:")[AttRtpmap(self)] >> *SP >> token[RtpmapPayload(self)] >> +SP >> token[RtpmapEncoding(self)] >> '/' >> uint_p[RtpmapClockrate(self)] >> *('/' >> token);
                att_sendrecv = chseq_p("sendrecv")[AttSendrecv(self)];
                //  attribute = (att-field ":" att-value) / att-field
                //att_unknown = att_field[AttUnknown(self)] >> !(':' >> att_value[UnknownValue(self)]);
                att_unknown = (att_field - chseq_p("rtpmap:") - chseq_p("sendrecv")) >> !(':' >> att_value);
    
                //  att-field =           token
                att_field = token;
    
                //  att-value =           byte-string
                att_value = byte_string;
    
                // --   sonstiges
                token = +token_char;
                //  token_char =  %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
                token_char = ch_p(0x21) | range_p(0x23,0x27) | range_p(0x2A,0x2B) | range_p(0x2D,0x2E) | range_p(0x30,0x39) | range_p(0x41,0x5A) | range_p(0x5E,0x7E);
                //  byte-string = 1*(%x01-09/%x0B-0C/%x0E-FF)   ;any byte except NUL, CR, or LF
                byte_string = +(anychar_p - ch_p(0) - eol_p);
                SP = ch_p(0x20);
    
                BOOST_SPIRIT_DEBUG_RULE( att_rtpmap );
                BOOST_SPIRIT_DEBUG_RULE( att_sendrecv );
                BOOST_SPIRIT_DEBUG_RULE( att_unknown );
                BOOST_SPIRIT_DEBUG_RULE( att_field );
                BOOST_SPIRIT_DEBUG_RULE( attribute );
            }
    
            spirit::rule< ScannerT > SP, token_char, byte_string, token, att_value, att_field, attribute, attribute_field, fmt, port, proto, media, media_field, sdp_file,
                att_rtpmap, att_sendrecv, att_unknown, media_description, skip_field;
    
            spirit::rule< ScannerT > const& start() const { return sdp_file; }
        };
    private:
        std::vector< MediaDescription >& m_storage;
    };
    
    std::string File2String( const char* filename )
    {
        std::ostringstream buf;
        std::ifstream file( filename );
        buf << file.rdbuf();
        return buf.str();
    }
    
    std::ostream& operator<<( std::ostream& out, const MediaField& mf )
    {
        out << mf.m_media << ": Port=" << mf.m_port << " Proto=" << mf.m_protocol;
        std::copy( mf.m_fmts.begin(), mf.m_fmts.end(), std::ostream_iterator< int >( out << " ", " " ) );
        return out;
    }
    
    std::ostream& operator<<( std::ostream& out, const MediaDescription& md )
    {
        out << md.m_mediaField << std::endl;
        out << md.m_attributeFields.size() << " Attribute:" << std::endl;
        std::copy( md.m_attributeFields.begin(), md.m_attributeFields.end(),
            std::ostream_iterator< AttributeField::Ptr >( out, "\n" ) );
        return out;
    }
    
    int main()
    {
        using namespace std;
        string in = File2String( "sdp_input.txt" );
        vector< MediaDescription > result;
        SdpParser sdpParser( result );
        if( parse( in.begin(), in.end(), sdpParser.use_parser< 0 >() ).full )
        {
            copy( result.begin(), result.end(), ostream_iterator< MediaDescription >( cout << "\nErgebnis:\n", "\n" ) );
            cout << endl;
        }
        else
            cout << "Parser Fehler" << endl;
        return 0;
    }
    

    das Füllen der Struktur vector<MediaDescription> habe ich mit Funktoren realisiert - das erschien mir hier sinnvoll, so muss SdpParser kein Template mehr sein und es sieht etwas durchgängiger aus.

    Die Datei sdp.h enthält die oben angesprochene Media-Description mit allen Unterklassen.

    #ifndef SDP_H_INCLUDE
    #define SDP_H_INCLUDE
    #include <iostream>
    #include <string>
    #include <vector>
    #include <boost/shared_ptr.hpp>
    
    struct MediaField
    {
        std::string m_media;
        std::string m_port;
        std::string m_protocol;
        std::vector< int > m_fmts; // fmt-Typ == int ??
    };
    
    namespace sdp
    {
        enum AttributeType
        {
            unknown, rtpmap, fmtp, ptime, sendrecv
        };
    }
    
    struct AttributeField // Base class aller Attribute
    {
        typedef boost::shared_ptr< AttributeField > Ptr;
        virtual ~AttributeField()=0 {} // pure abstract
        virtual sdp::AttributeType Type() const =0;
        friend std::ostream& operator<<( std::ostream& out, const AttributeField& att )
        {
            att.print( out );
            return out;
        }
    protected:
        virtual void print( std::ostream& out ) const
        {
            out << "Attribut Type(" << Type() << ")";
        }
    };
    std::ostream& operator<<( std::ostream& out, AttributeField::Ptr af )
    {
        return out << *af;
    }
    
    struct AttributeRtpmap : public AttributeField
    {
        virtual sdp::AttributeType Type() const { return sdp::rtpmap; }
        std::string m_payloadType;
        std::string m_encodingName;
        int m_clockrate;
        std::vector< int > m_encodingParams; // Typ == int?
    protected:
        virtual void print( std::ostream& out ) const
        {
            out << "Attribut rtpmap: " << m_payloadType << " " << m_encodingName << "/" << m_clockrate;
        }
    };
    
    struct AttributeSendrecv : public AttributeField
    {
        virtual sdp::AttributeType Type() const { return sdp::sendrecv; }
    protected:
        virtual void print( std::ostream& out ) const
        {
            out << "Attribut sendrecv";
        }
    };
    
    struct AttributeUnknown : public AttributeField
    {
        template< typename I >
        AttributeUnknown( I first, I last )
            : m_paramName( first, last )
            , m_paramValue()
        {}
        virtual sdp::AttributeType Type() const { return sdp::unknown; }
        std::string m_paramName;
        std::string m_paramValue;
    protected:
        virtual void print( std::ostream& out ) const
        {
            out << "Attribut name=" << m_paramName;
            if( !m_paramValue.empty() )
                out << " value=" << m_paramValue;
        }
    };
    // .. weitere Attribut-Strukturen
    struct AttributeFmtp; // to code
    struct AttributePtime;
    
    struct MediaDescription
    {
        MediaField m_mediaField;
        std::vector< AttributeField::Ptr > m_attributeFields;
    };
    #endif
    

    Ich kenne zwar Deine Anforderungen nicht, aber ich glaube, dass diese Struktur besser zu Deinem Problem passt, da die Domain SDP besser abgebildet ist.

    Der Code ist i.A. so gemacht, dass er nur Attribute abspeichert, die auch bekannt sind; möchtest Du alle Attributtypen speichern, so musst Du im 1.Listing die Zeile 93 durch Zeile 92 ersetzen.

    Der Inhalt der Datei sdp_input.txt ist diesmal:

    m=audio 45678 RTP/AVP 0 3 8 101
    i=das ist eine Test-Datei
    a=rtpmap: 0 ULAW/8000
    a=rtpmap: 3 GSM/8000
    a=rtpmap: 8 ALAW/8000
    a=rtpmap: 101 telephone-event/8000
    a=fmtp: 101 0-16
    a=ptime: 20
    a=rumpelstilzchen
    a=sendrecv
    m=video 20001 RTP/AVP 31
    a=rtpmap:31 H261/90000
    

    .. und die Ausgabe ist:

    Ergebnis:
    audio: Port=45678 Proto=RTP/AVP 0 3 8 101
    5 Attribute:
    Attribut rtpmap: 0 ULAW/8000
    Attribut rtpmap: 3 GSM/8000
    Attribut rtpmap: 8 ALAW/8000
    Attribut rtpmap: 101 telephone-event/8000
    Attribut sendrecv
    
    video: Port=20001 Proto=RTP/AVP 31
    1 Attribute:
    Attribut rtpmap: 31 H261/90000
    

    Gruß
    Werner



  • Puuhhh...
    Das wird dann mal ein langer Tag und eine noch längere Nacht bis ich da komplett hintergestiegen bin.

    Aber jetzt erst mal in boost einarbeiten...scheint ja doch recht wichtig und sinnvoll zu sein die lib zu beherrschen.



  • mr. main schrieb:

    Aber jetzt erst mal in boost einarbeiten...scheint ja doch recht wichtig und sinnvoll zu sein die lib zu beherrschen.

    Das ist eine ganze Sammlung von libs - einige sehr sinnvoll, andere eher spezialisiert und daher nicht unbedingt so wichtig.


Log in to reply