| 210 | } |
| 211 | |
| 212 | DecodeResult H264HwDecoder::decode() FL_NOEXCEPT { |
| 213 | if (!mImpl->ready || !mImpl->decoder) return DecodeResult::Error; |
| 214 | if (mImpl->frameDecoded) return DecodeResult::EndOfStream; |
| 215 | |
| 216 | const fl::u16 w = mImpl->info.width; |
| 217 | const fl::u16 h = mImpl->info.height; |
| 218 | |
| 219 | // Split Annex B stream into individual NAL units |
| 220 | fl::vector<NalUnit> nalUnits = splitAnnexB(mImpl->annexB); |
| 221 | if (nalUnits.empty()) { |
| 222 | mImpl->error = true; |
| 223 | mImpl->errorMsg = "No NAL units found in Annex B stream"; |
| 224 | return DecodeResult::Error; |
| 225 | } |
| 226 | |
| 227 | // Feed each NAL unit to decoder until we get a frame |
| 228 | for (fl::size i = 0; i < nalUnits.size(); i++) { |
| 229 | esp_h264_dec_in_frame_t in_frame = {}; |
| 230 | in_frame.raw_data.buffer = const_cast<fl::u8*>(nalUnits[i].data); |
| 231 | in_frame.raw_data.len = static_cast<fl::u32>(nalUnits[i].len); |
| 232 | |
| 233 | esp_h264_dec_out_frame_t out_frame = {}; |
| 234 | |
| 235 | esp_h264_err_t err = esp_h264_dec_process(mImpl->decoder, &in_frame, &out_frame); |
| 236 | if (err != ESP_H264_ERR_OK) { |
| 237 | continue; // SPS/PPS NALUs may not produce output |
| 238 | } |
| 239 | |
| 240 | if (out_frame.out_size > 0 && out_frame.outbuf != nullptr) { |
| 241 | // YUV420P (I420) → RGB888 conversion |
| 242 | const fl::u32 pixelCount = static_cast<fl::u32>(w) * static_cast<fl::u32>(h); |
| 243 | const fl::u8* yPlane = out_frame.outbuf; |
| 244 | const fl::u8* uPlane = out_frame.outbuf + pixelCount; |
| 245 | const fl::u8* vPlane = uPlane + pixelCount / 4; |
| 246 | |
| 247 | fl::vector<fl::u8> rgbBuf(pixelCount * 3) FL_NOEXCEPT; |
| 248 | fl::u8* dst = rgbBuf.data(); |
| 249 | |
| 250 | for (fl::u16 row = 0; row < h; row++) { |
| 251 | for (fl::u16 col = 0; col < w; col++) { |
| 252 | int y = yPlane[row * w + col]; |
| 253 | int u = uPlane[(row / 2) * (w / 2) + (col / 2)]; |
| 254 | int v = vPlane[(row / 2) * (w / 2) + (col / 2)]; |
| 255 | |
| 256 | // BT.601 YUV→RGB fixed-point |
| 257 | int d = u - 128; |
| 258 | int e = v - 128; |
| 259 | *dst++ = clampU8(y + ((359 * e + 128) >> 8)); // R |
| 260 | *dst++ = clampU8(y - ((88 * d + 183 * e + 128) >> 8)); // G |
| 261 | *dst++ = clampU8(y + ((454 * d + 128) >> 8)); // B |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | mImpl->currentFrame = Frame(rgbBuf.data(), w, h, PixelFormat::RGB888, 0); |
| 266 | mImpl->frameDecoded = true; |
| 267 | return DecodeResult::Success; |
| 268 | } |
| 269 | } |