从RTSP MJPEG流构建JPEG图像

5
我正在尝试从IP摄像机的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中解释的。这正确吗?
  • 我应该使用片段偏移字段来构建我的图像,还是只需将有效载荷数据附加到下一个数据包。
  • 数据包超出了Libcurl数据包的长度。这是预期的吗。

谢谢!

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

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


希望能够有一个流的样本来查看。 - soulseekah
1个回答

4

我从curl获取的数据中有额外的0x0d字符。所有额外的0x0d字符都出现在0x0a字符之前。我删除了所有在0x0a之前出现的0x0d字符,然后我的算法就正常工作了。我通过比较curl和Wire Shark给出的数据输出来发现了这个问题。看起来curl插入了额外的字符,可能是一个bug。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接