从RTSP MJPEG流构造JPEG图像

时间:2015-01-21 15:44:34

标签: c curl rtsp ip-camera mjpeg

我正在尝试从IP Camera的RTSP MJPEG流构建JPEG图像。我使用CURL获取RTSP数据,然后我关注RTP Payload Format for JPEG-compressed video RFC

我已经成功地能够形成没有量化表的JPEG图像。但是对于128类型的Q,我的算法无法正确组装JPEG图像。

我知道有像ffmpeg和gstreamer这样的库可以执行这种解码,但由于其他一些问题,我无法使用它们。我也查看了其他帖子,我认为我正在正确解码流。但在JPEG解压缩后,图像看起来像这样。背景图像看似正确,但图片上散布的水平条带不正确。

Corrupted JPEG with Type 65 and Q 128

这是我正在使用的代码段:

    while(!firstPacketFound) {
        std::cout <<  "-----------RTP Packet: " << index++ << " starting at " << startOfRTPPacketIterator << "---------"<< endl;
        isFirstPacket = false; isLastPacket = false;
        // 0 - LibCurl header
        // 1 - channel identifier
        // 2, 3 - packet length
        uint16_T packet_length_msb = f[ startOfRTPPacketIterator+2];
        uint16_T packet_length_lsb = f[ startOfRTPPacketIterator+3];
        packet_length = packet_length_msb << 8 | packet_length_lsb;
        std::cout << "Packet Size: " << packet_length << endl;
        // 4 - RTP Header - version + pasdding - generally 80
        // 5 - Marker Bit + Payload  26 for JPEG
        uint8_T rtp1 = f[ startOfRTPPacketIterator+4];
        uint8_T payloadandmarkerbit = f[startOfRTPPacketIterator+5];
        uint8_T markerbit =(payloadandmarkerbit >> 7) ;
        uint8_T payload = payloadandmarkerbit & 0x7F;
        std::cout << "Marker byte : " << int(payloadandmarkerbit) << endl;
        if( payload != 26) {
            cout << "NOT JPEG packet" << endl;
        }
        // 6 & 7 - sequence 
        // 8, 9, 10,11 - timestamp
        //12, 13, 14, 15 - ssrc
        uint16_t sequenceNumber_m = f[ startOfRTPPacketIterator+6];
        uint16_t sequenceNumber_l = f[ startOfRTPPacketIterator+7];
        uint16_t sequenceNumber = (sequenceNumber_m << 8) | sequenceNumber_l;
        std::cout << "Sequence Number : " << sequenceNumber <<endl;

        uint32_t timestamp1 = f[ startOfRTPPacketIterator+8];
        uint32_t timestamp2 = f[ startOfRTPPacketIterator+9];
        uint32_t timestamp3 = f[ startOfRTPPacketIterator+10];
        uint32_t timestamp4 = f[ startOfRTPPacketIterator+11];
        uint32_t timestamp = (timestamp1 <<24) | (timestamp2 <<16) | (timestamp3<<8) | timestamp4;
         std::cout << "Timestamp : " << timestamp <<endl;

        //--------------------JPEG Header ----------------------/
        // 16 - type specific
        // 17, 18, 19, - framgemnt offset
        // 20 - type
        // 21 - Q
        // 22 - Width
        //23- Height
        uint32_t framgent_offset_1 = f[startOfRTPPacketIterator+17];
        uint32_t framgent_offset_2 = f[startOfRTPPacketIterator+18];
        uint32_t framgent_offset_3 = f[startOfRTPPacketIterator+19];
        uint32_t framgent_offset = ((framgent_offset_1 << 16) | (framgent_offset_2 << 8) | (framgent_offset_3));
        if (framgent_offset == 0)
        {
            isFirstPacket = true;
        }

        cout << "Fragment Offset: " << framgent_offset <<endl;
        uint8_t type_specific = f[ startOfRTPPacketIterator+16];
        cout << "type_specific: " << int(type_specific) <<endl;
        uint8_t type = f[startOfRTPPacketIterator+20];
        cout << "type : " << int(type) <<endl;
        uint8_t Q = f[ startOfRTPPacketIterator+21];
        cout << "Q: " << int(Q) <<endl;
        uint8_t width = f[ startOfRTPPacketIterator+22];
        cout << "width : " << int(width*8) <<endl;
        uint8_t height = f[ startOfRTPPacketIterator+23];
        cout << "height : " << int(height*8) <<endl;

        // Check for restart markers
        if ( type > 63) {
        // For 64-127, restart markers are present in the JPEG data
        //------------------Restart Marker Header------------------/
        // 24, 25 - restart interval
        // 26, 27 - F+L+ Restart Count
        uint16_T restart_interval_m = uint16_T(f[startOfRTPPacketIterator+24]);
        uint16_T restart_interval_l = uint16_T(f[startOfRTPPacketIterator+25]);
        restart_interval = (restart_interval_m <<8 ) | restart_interval_l;
        uint16_T restart_count_m = uint16_T(f[startOfRTPPacketIterator+26]);
        uint16_T restart_count_l = uint16_T(f[startOfRTPPacketIterator+27]);
        uint16_T restart_count = ((restart_count_m <<8 ) | restart_count_l) & 0x3F;


        uint8_T F = restart_count_m  >> 7;
        uint8_T L = (restart_count_m & 0x40) >> 6;
        cout << "restart_interval : " << restart_interval <<endl;
        cout << "restart_count : " << restart_count <<endl;
        cout << "F : " << int(F) <<endl;
        cout << "L : " << int(L) <<endl;
        }

        //Check for quantization table in the first packet of the frame
        if ( Q > 127) {
                        //--------------Quantization Header -----------------------/
        // 28 - MBZ
        // 29- Precision 
        // 30, 31 - length
        // ...Quantization data
        uint8_t MBZ = f[startOfRTPPacketIterator+28];
        uint8_t precision = f[startOfRTPPacketIterator+29];
        uint16_T qauntization_data_length = uint16_T(f[startOfRTPPacketIterator+30]) << 8 | uint16_T(f[startOfRTPPacketIterator+31])  ;

        cout << "MBZ : " << int(MBZ) << endl;
        cout << "precision : " << int(precision) << endl;
        cout << "qauntization_data_length : " << qauntization_data_length << endl;
        }

        int indexOfPayload;
        indexOfPayload = 28 ;




       if (isFirstPacket) {
           cout << "First packet found" << endl;
           memcpy(lqt,&f[startOfRTPPacketIterator+32],64);
           memcpy(cqt,&f[startOfRTPPacketIterator+32+65],64);
           //  MakeTables(Q,lqt,cqt);
           unsigned char * p = new unsigned char[3000];
           int sizeOfHeaders =  MakeHeaders(p,type,width, height, lqt, cqt, restart_interval);
           cout << "Size of headers:"<< sizeOfHeaders <<endl;
           jpegPayload.insert(jpegPayload.end(),p,p+sizeOfHeaders+1);
           delete p;
            indexOfPayload = 32 +128 ;

       }
         int laststartOfRTPPacketIterator = startOfRTPPacketIterator;
          // Look for start of frame marker
         std::vector<uint8_t>::iterator startOfRTPPacketIterator2;
       // startOfRTPPacketIterator2 = std::find(f.begin() + startOfRTPPacketIterator +packet_length ,f.end(),0x24);
         startOfRTPPacketIterator2 = std::search(f.begin() + startOfRTPPacketIterator +packet_length,f.end(),startOfStreamMarker.begin(),startOfStreamMarker.begin()+2);
         startOfRTPPacketIterator = (int)std::distance(f.begin(),startOfRTPPacketIterator2);

         cout << "Adding payload data" << endl;
         cout << "Starting at " << (laststartOfRTPPacketIterator+indexOfPayload)  << endl;
         cout << "Ending at " << startOfRTPPacketIterator  << endl;
         cout << "Size of vector before : " <<jpegPayload.size() <<endl;
        // jpegPayload.reserve(framgent_offset);
       jpegPayload.insert(jpegPayload.end(), &f[laststartOfRTPPacketIterator+indexOfPayload], &f[startOfRTPPacketIterator]);
        cout << "Size of vector after : " <<jpegPayload.size() <<endl;

        if (markerbit) {
            firstPacketFound = true;
            cout << "Last packet found" << endl;
            isLastPacket = true;

            FILE * image = fopen("image.jpeg","wb");
            fwrite(&jpegPayload[0],sizeof(char), jpegPayload.size(), image);
            fclose(image);            

        }  

任何想法可能会出错?

  • 我只从第一个RTP数据包复制量化表。这就是我从RFC中解释的内容。这是对的。
  • 我应该使用Fragment Offset字段构建我的图像,还是只保留附加的有效负载数据,直到我点击下一个数据包。
  • 数据包超出Libcurl数据包的长度。这是预期的。

谢谢!

更新:我还注意到有效的应用程序使用UDP流,而我将传输设置为TCP - const char *transport = "RTP/AVP/TCP;unicast;interleaved=0-1";

这会导致任何差异吗?有些摄像机只支持UDP而不支持TCP。

1 个答案:

答案 0 :(得分:3)

我从curl获得的数据中有额外的0x0d。所有额外的0x0d都在0x0a之前。我删除了0x0a之前的所有0x0d然后我的算法工作正常。我通过比较curl和wire shark给出的数据输出来计算出来。这似乎是插入额外字符的卷曲中的一个错误。