@@ -68,16 +68,10 @@ static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED;
68
68
#ifndef CAM_SOI_PROBE_BYTES
69
69
#define CAM_SOI_PROBE_BYTES 32
70
70
#endif
71
-
72
- /* Number of bytes copied to SRAM for EOI validation when capturing
73
- * directly to PSRAM. Tunable to probe more of the frame tail if needed. */
74
- #ifndef CAM_EOI_PROBE_BYTES
75
- #define CAM_EOI_PROBE_BYTES 32
76
- #endif
77
-
78
71
/*
79
- * PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on the
80
- * SOI probe region so cached reads see the data written by DMA.
72
+ * PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on
73
+ * PSRAM regions that the CPU will read so cached reads see the data written
74
+ * by DMA.
81
75
*/
82
76
83
77
static inline size_t dcache_line_size (void )
@@ -129,12 +123,19 @@ static inline void cam_drop_psram_cache(void *addr, size_t len)
129
123
#define CAM_WARN_THROTTLE (counter , first ) do { (void)(counter); } while (0)
130
124
#endif
131
125
132
- /* JPEG markers in little-endian order (ESP32 ). */
126
+ /* JPEG markers (byte- order independent ). */
133
127
static const uint8_t JPEG_SOI_MARKER [] = {0xFF , 0xD8 , 0xFF }; /* SOI = FF D8 FF */
134
128
#define JPEG_SOI_MARKER_LEN (3)
135
- static const uint16_t JPEG_EOI_MARKER = 0xD9FF ; /* EOI = FF D9 */
129
+ static const uint8_t JPEG_EOI_BYTES [] = { 0xFF , 0xD9 }; /* EOI = FF D9 */
136
130
#define JPEG_EOI_MARKER_LEN (2)
137
131
132
+ /* Compute the scan window for JPEG EOI detection in PSRAM. */
133
+ static inline size_t eoi_probe_window (size_t half , size_t frame_len )
134
+ {
135
+ size_t w = half + (JPEG_EOI_MARKER_LEN - 1 );
136
+ return w > frame_len ? frame_len : w ;
137
+ }
138
+
138
139
static int cam_verify_jpeg_soi (const uint8_t * inbuf , uint32_t length )
139
140
{
140
141
static uint16_t warn_soi_miss_cnt = 0 ;
@@ -156,19 +157,54 @@ static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length)
156
157
return -1 ;
157
158
}
158
159
159
- static int cam_verify_jpeg_eoi (const uint8_t * inbuf , uint32_t length )
160
+ static int cam_verify_jpeg_eoi (const uint8_t * inbuf , uint32_t length , bool search_forward )
160
161
{
161
162
if (length < JPEG_EOI_MARKER_LEN ) {
162
163
return -1 ;
163
164
}
164
165
165
- int offset = -1 ;
166
- uint8_t * dptr = (uint8_t * )inbuf + length - JPEG_EOI_MARKER_LEN ;
167
- while (dptr > inbuf ) {
168
- if (memcmp (dptr , & JPEG_EOI_MARKER , JPEG_EOI_MARKER_LEN ) == 0 ) {
169
- offset = dptr - inbuf ;
170
- //ESP_LOGW(TAG, "EOI: %d", length - (offset + 2));
171
- return offset ;
166
+ if (search_forward ) {
167
+ /* Scan forward to honor the earliest marker in the buffer. This avoids
168
+ * returning an EOI that belongs to a larger previous frame when the tail
169
+ * of that frame still resides in PSRAM. JPEG data is pseudo random, so
170
+ * the first marker byte appears rarely; test four positions per load to
171
+ * reduce memory traffic. */
172
+ const uint8_t * pat = JPEG_EOI_BYTES ;
173
+ const uint32_t A = pat [0 ] * 0x01010101u ;
174
+ const uint32_t ONE = 0x01010101u ;
175
+ const uint32_t HIGH = 0x80808080u ;
176
+ uint32_t i = 0 ;
177
+ while (i + 4 <= length ) {
178
+ uint32_t w ;
179
+ memcpy (& w , inbuf + i , 4 ); /* unaligned load is allowed */
180
+ uint32_t x = w ^ A ; /* identify bytes equal to first marker byte */
181
+ uint32_t m = (~x & (x - ONE )) & HIGH ; /* mask has high bit set for candidate bytes */
182
+ while (m ) { /* handle only candidates to avoid unnecessary memcmp calls */
183
+ unsigned off = __builtin_ctz (m ) >> 3 ;
184
+ uint32_t pos = i + off ;
185
+ if (pos + JPEG_EOI_MARKER_LEN <= length &&
186
+ memcmp (inbuf + pos , pat , JPEG_EOI_MARKER_LEN ) == 0 ) {
187
+ return pos ;
188
+ }
189
+ m &= m - 1 ; /* clear processed candidate */
190
+ }
191
+ i += 4 ;
192
+ }
193
+ for (; i + JPEG_EOI_MARKER_LEN <= length ; i ++ ) {
194
+ if (memcmp (inbuf + i , pat , JPEG_EOI_MARKER_LEN ) == 0 ) {
195
+ return i ;
196
+ }
197
+ }
198
+ return -1 ;
199
+ }
200
+
201
+ const uint8_t * dptr = inbuf + length - JPEG_EOI_MARKER_LEN ;
202
+ while (dptr >= inbuf ) {
203
+ if (memcmp (dptr , JPEG_EOI_BYTES , JPEG_EOI_MARKER_LEN ) == 0 ) {
204
+ return dptr - inbuf ;
205
+ }
206
+ if (dptr == inbuf ) {
207
+ break ;
172
208
}
173
209
dptr -- ;
174
210
}
@@ -707,27 +743,26 @@ camera_fb_t *cam_take(TickType_t timeout)
707
743
/* find the end marker for JPEG. Data after that can be discarded */
708
744
int offset_e = -1 ;
709
745
if (cam_obj -> psram_mode ) {
710
- size_t probe_len = dma_buffer -> len ;
711
- if (probe_len > CAM_EOI_PROBE_BYTES ) {
712
- probe_len = CAM_EOI_PROBE_BYTES ;
713
- }
714
- if (probe_len == 0 ) {
746
+ /* Search forward from (JPEG_EOI_MARKER_LEN - 1) bytes before the final
747
+ * DMA block. We prefer forward search to pick the earliest EOI in the
748
+ * last half-buffer, avoiding stale markers from a larger prior frame. */
749
+ size_t probe_len = eoi_probe_window (cam_obj -> dma_half_buffer_size ,
750
+ dma_buffer -> len );
751
+ if (probe_len < JPEG_EOI_MARKER_LEN ) {
715
752
goto skip_eoi_check ;
716
753
}
717
- cam_drop_psram_cache (dma_buffer -> buf + dma_buffer -> len - probe_len , probe_len );
718
-
719
- uint8_t eoi_probe [CAM_EOI_PROBE_BYTES ];
720
- memcpy (eoi_probe , dma_buffer -> buf + dma_buffer -> len - probe_len , probe_len );
721
- int off = cam_verify_jpeg_eoi (eoi_probe , probe_len );
754
+ uint8_t * probe_start = dma_buffer -> buf + dma_buffer -> len - probe_len ;
755
+ cam_drop_psram_cache (probe_start , probe_len );
756
+ int off = cam_verify_jpeg_eoi (probe_start , probe_len , true);
722
757
if (off >= 0 ) {
723
758
offset_e = dma_buffer -> len - probe_len + off ;
724
759
}
725
760
} else {
726
- offset_e = cam_verify_jpeg_eoi (dma_buffer -> buf , dma_buffer -> len );
761
+ offset_e = cam_verify_jpeg_eoi (dma_buffer -> buf , dma_buffer -> len , false );
727
762
}
728
763
729
764
if (offset_e >= 0 ) {
730
- dma_buffer -> len = offset_e + sizeof ( JPEG_EOI_MARKER ) ;
765
+ dma_buffer -> len = offset_e + JPEG_EOI_MARKER_LEN ;
731
766
if (cam_obj -> psram_mode ) {
732
767
/* DMA may bypass cache, ensure full frame is visible */
733
768
cam_drop_psram_cache (dma_buffer -> buf , dma_buffer -> len );
0 commit comments