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 | |