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