| 1 | /* --------------------------------------------------------------------------- |
| 2 | ** This software is in the public domain, furnished "as is", without technical |
| 3 | ** support, and with no warranty, express or implied, as to its usefulness for |
| 4 | ** any purpose. |
| 5 | ** |
| 6 | ** MJPEGVideoSource.cpp |
| 7 | ** |
| 8 | ** V4L2 RTSP streamer |
| 9 | ** |
| 10 | ** MJPEG Source for RTSP server |
| 11 | ** |
| 12 | ** -------------------------------------------------------------------------*/ |
| 13 | |
| 14 | #include "MJPEGVideoSource.h" |
| 15 | |
| 16 | |
| 17 | void MJPEGVideoSource::afterGettingFrame(unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned durationInMicroseconds) |
| 18 | { |
| 19 | int = 0; |
| 20 | fFrameSize = 0; |
| 21 | |
| 22 | unsigned int i = 0; |
| 23 | while ( (i<frameSize) && (headerSize==0) ) { |
| 24 | // SOF |
| 25 | if ( ((i+11) < frameSize) && (fTo[i] == 0xFF) && (fTo[i+1] == 0xC0) ) { |
| 26 | int length = (fTo[i+2]<<8)|(fTo[i+3]); |
| 27 | LOG(DEBUG) << "SOF length:" << length; |
| 28 | |
| 29 | m_height = (fTo[i+5]<<5)|(fTo[i+6]>>3); |
| 30 | m_width = (fTo[i+7]<<5)|(fTo[i+8]>>3); |
| 31 | |
| 32 | int hv_subsampling = fTo[i+11]; |
| 33 | if (hv_subsampling == 0x21 ) { |
| 34 | m_type = 0; // JPEG 4:2:2 |
| 35 | } else if (hv_subsampling == 0x22 ) { |
| 36 | m_type = 1; // JPEG 4:2:0 |
| 37 | } else { |
| 38 | LOG(NOTICE) << "not managed sampling:0x" << std::hex << hv_subsampling; |
| 39 | m_type = 255; |
| 40 | } |
| 41 | |
| 42 | int precision = fTo[i+4]; |
| 43 | LOG(INFO) << "width:" << (int)(m_width<<3) << " height:" << (int)(m_height<<3) << " type:" << (int)m_type << " precision:" << precision; |
| 44 | |
| 45 | i+=length+2; |
| 46 | } |
| 47 | // DQT |
| 48 | else if (((i+5) < frameSize) && (fTo[i] == 0xFF) && (fTo[i+1] == 0xDB)) { |
| 49 | int length = (fTo[i+2]<<8)|(fTo[i+3]); |
| 50 | LOG(DEBUG) << "DQT length:" << length; |
| 51 | |
| 52 | unsigned int precision = (fTo[i+4]&0xf0)<<4; |
| 53 | unsigned int quantIdx = fTo[i+4]&0x0f; |
| 54 | unsigned int quantSize = 64*(precision+1); |
| 55 | if (quantSize*quantIdx+quantSize <= sizeof(m_qTable)) { |
| 56 | if ( (i+2+length) < frameSize) { |
| 57 | memcpy(m_qTable + quantSize*quantIdx, fTo + i + 5, length-3); |
| 58 | LOG(DEBUG) << "Quantization table idx:" << quantIdx << " precision:" << precision << " size:" << quantSize << " total size:" << m_qTableSize; |
| 59 | if (quantSize*quantIdx+quantSize > m_qTableSize) { |
| 60 | m_qTableSize = quantSize*quantIdx+quantSize; |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | i+=length+2; |
| 66 | } |
| 67 | // SOS |
| 68 | else if ( ((i+3) < frameSize) && (fTo[i] == 0xFF) && (fTo[i+1] == 0xDA) ) { |
| 69 | int length = (fTo[i+2]<<8)|(fTo[i+3]); |
| 70 | LOG(DEBUG) << "SOS length:" << length; |
| 71 | |
| 72 | headerSize = i+length+2; |
| 73 | // DRI |
| 74 | } else if ( ((i+5) < frameSize) && (fTo[i] == 0xFF) && (fTo[i+1] == 0xDD) ) { |
| 75 | m_type |= 0x40; |
| 76 | int length = (fTo[i+2]<<8)|(fTo[i+3]); |
| 77 | m_restartInterval = (fTo[i+4]<<8)|(fTo[i+5]); |
| 78 | LOG(DEBUG) << "DRI restartInterval:" << m_restartInterval; |
| 79 | i+=length+2; |
| 80 | } else { |
| 81 | i++; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | if (headerSize != 0) { |
| 86 | LOG(DEBUG) << "headerSize:" << headerSize; |
| 87 | fFrameSize = frameSize - headerSize; |
| 88 | memmove( fTo, fTo + headerSize, fFrameSize ); |
| 89 | } else { |
| 90 | LOG(NOTICE) << "Bad header => dropping frame" ; |
| 91 | } |
| 92 | |
| 93 | fNumTruncatedBytes = numTruncatedBytes; |
| 94 | fPresentationTime = presentationTime; |
| 95 | fDurationInMicroseconds = durationInMicroseconds; |
| 96 | afterGetting(this); |
| 97 | } |
| 98 | |
| 99 | u_int8_t const* MJPEGVideoSource::quantizationTables( u_int8_t& precision, u_int16_t& length ) |
| 100 | { |
| 101 | length = 0; |
| 102 | precision = 0; |
| 103 | if (m_qTableSize > 0) |
| 104 | { |
| 105 | length = m_qTableSize; |
| 106 | precision = m_precision; |
| 107 | } |
| 108 | return m_qTable; |
| 109 | } |
| 110 | |