Ты — тестер и исправитель кода. Твоя задача — проверять код на ошибки, баги и потенциальные проблемы, а также исправлять их.
#include "app_param.h"
#include "luckfox_time.h"
#include "save_images.h"

// ------------------------------

extern int rknn_init_model();
extern int rknn_infer_image(unsigned char* input_data, detect_result_group_t* detect_result_group);
extern void rknn_release();

extern void _FIND_NUMBER(detect_result_group_t* grp);

#define MAX_FRAMES_QUEUE 18

typedef struct {
  const char* url;
  rknn_task_t* task;
  int id;
} camera_thread_args_t;

typedef struct {
  int run; // Флаг работы потока
  int queue_size; // Текущее количество кадров в буфере
  int write_pos; // Позиция записи нового кадра
  int read_pos; // Позиция чтения кадра для обработки
  pthread_mutex_t queue_mutex; // Мьютекс для безопасного доступа к буферу
  sem_t sem_rknn; // Семафор для RKNN-потока
  int frame_thread[MAX_FRAMES_QUEUE]; // Буфер кадров
  AVFrame* frame_queue[MAX_FRAMES_QUEUE]; // Буфер кадров
} rknn_task_t;

void add_frame_to_queue(rknn_task_t* task, int thread_id, AVFrame* frame)
{
  AVFrame* new_frame = av_frame_alloc();
  if (!new_frame) {
    fprintf(stderr, "Failed to alloc new AVFrame\n");
    return;
  }

  if (av_frame_ref(new_frame, frame) < 0) {
    fprintf(stderr, "Failed to ref frame\n");
    av_frame_free(&new_frame);
    return;
  }

  pthread_mutex_lock(&task->queue_mutex);

  // Если очередь заполнена - освобождаем старый кадр (перезаписываем oldest)
  if (task->queue_size == MAX_FRAMES_QUEUE) {

    av_frame_free(&task->frame_queue[task->write_pos]);
    task->frame_queue[task->write_pos] = NULL;
    task->frame_thread[task->write_pos] = 0;
    task->read_pos = (task->read_pos + 1) % MAX_FRAMES_QUEUE;

  } else {
    task->queue_size++;
  }

  task->frame_queue[task->write_pos] = new_frame;
  task->frame_thread[task->write_pos] = thread_id;
  task->write_pos = (task->write_pos + 1) % MAX_FRAMES_QUEUE;

  // printf("		2 [Writer %d] wp=%d rp=%d size=%d\n", thread_id, task->write_pos, task->read_pos, task->queue_size);

  pthread_mutex_unlock(&task->queue_mutex);

  // Уведомление о новом кадре
  sem_post(&task->sem_rknn);
}

// Функция возвращает AVFrame*, если удалось, или NULL если очередь пуста
AVFrame* get_frame_from_queue(rknn_task_t* task, int* thread_id)
{
  pthread_mutex_lock(&task->queue_mutex);

  if (task->queue_size == 0) {
    pthread_mutex_unlock(&task->queue_mutex);
    return NULL;
  }

  AVFrame* frame = task->frame_queue[task->read_pos];

  if (thread_id) {
    *thread_id = task->frame_thread[task->read_pos];
  }

  task->frame_queue[task->read_pos] = NULL;
  task->frame_thread[task->read_pos] = 0;

  task->read_pos = (task->read_pos + 1) % MAX_FRAMES_QUEUE;
  task->queue_size--;

  // printf("			3 [Reader] rp=%d wp=%d size=%d\n", task->read_pos, task->write_pos, task->queue_size);

  pthread_mutex_unlock(&task->queue_mutex);

  return frame;
}

void init_rknn_task(rknn_task_t* task)
{
  task->run = 1;

  task->queue_size = 0;
  task->write_pos = 0;
  task->read_pos = 0;

  sem_init(&task->sem_rknn, 0, 0);

  for (int i = 0; i < MAX_FRAMES_QUEUE; i++) {
    task->frame_queue[i] = NULL;
    task->frame_thread[i] = 0;
  }

  pthread_mutex_init(&task->queue_mutex, NULL);
}

void cleanup_rknn_task(rknn_task_t* task)
{
  task->run = 0;

  // Очистка оставшихся кадров
  pthread_mutex_lock(&task->queue_mutex);
  for (int i = 0; i < task->queue_size; i++) {
    av_frame_free(&task->frame_queue[(task->read_pos + i) % MAX_FRAMES_QUEUE]);
    task->frame_queue[(task->read_pos + i) % MAX_FRAMES_QUEUE] = NULL;
    task->frame_thread[(task->read_pos + i) % MAX_FRAMES_QUEUE] = 0;
  }
  task->queue_size = 0;
  pthread_mutex_unlock(&task->queue_mutex);

  sem_destroy(&task->sem_rknn);

  pthread_mutex_destroy(&task->queue_mutex);
}

void* _rknn_thread(void* arg)
{

  rknn_task_t* task = (rknn_task_t*)arg;

  int ret = rknn_init_model();
  if (ret != 0) {
    printf("init failed\n");
    pthread_exit(NULL);
  }
  int thread_id = 0;

  AVFrame* frame;
  detect_result_group_t detect_result_group;
  while (task->run == 1) {
    // Ожидаем разрешения (если занят — блокируем поток)
    sem_wait(&task->sem_rknn);

    while ((frame = get_frame_from_queue(task, &thread_id)) != NULL) {

      // save_frame_as_jpeg(frame, frame->width, frame->height, counter+thread_id) ;

      // инференс - 100 милисекунд..
      int ret = rknn_infer_image((unsigned char*)frame->data[0], &detect_result_group);

      if (ret == 0) {
        _FIND_NUMBER(&detect_result_group);
      }

      av_frame_free(&frame);
      // printf("					5 %s: длительность: %s %.3f\n", GLOBAL_TIME_PRINT(), GLOBAL_TIME_END_PRNT(START3), GLOBAL_TIME_END(START3));
    }
  }

  rknn_release();

  pthread_exit(NULL);
}

void cleanup_ffmpeg_resources(AVFormatContext** fmt_ctx, AVCodecContext** codec_ctx, struct SwsContext** sws_ctx, AVFrame** frame, AVFrame** rgb_frame, AVPacket** pkt)
{
  if (*frame) {
    av_frame_free(frame);
    *frame = NULL;
  }
  if (*rgb_frame) {
    av_frame_free(rgb_frame);
    *rgb_frame = NULL;
  }
  if (*pkt) {
    av_packet_free(pkt);
    *pkt = NULL;
  }
  if (*sws_ctx) {
    sws_freeContext(*sws_ctx);
    *sws_ctx = NULL;
  }
  if (*codec_ctx) {
    avcodec_free_context(codec_ctx);
    *codec_ctx = NULL;
  }
  if (*fmt_ctx) {
    avformat_close_input(fmt_ctx);
    *fmt_ctx = NULL;
  }
}

int camera_open_stream(const char* url, AVFormatContext** fmt_ctx, int* video_stream_idx, AVCodecContext** codec_ctx, struct SwsContext** sws_ctx, AVFrame** frame, AVFrame** rgb_frame, AVPacket** pkt)
{

  AVDictionary* options = NULL;
  /*
  FPS	I-frame interval	Ключевой кадр появляется раз в
  3	6	2 секунды
  3	9	3 секунды
  4	6	1.5 секунды
  При обработке в 250-300 мс, оптимально ограничить FPS примерно 3-4.
  Уменьшить интервал ключевых кадров, чтобы не ждать долго повторения ключевого кадра.

  Framerate: установите 9 fps (как планируете).
  I-frame interval: желательно, чтобы ключевой кадр появлялся как минимум раз в секунду.
  При 9 fps достаточно интервала в 9 (ключевой кадр каждый 1 секундный блок).
  Можно даже уменьшить до 6 (ключевой кадр каждые 2/3 секунды), если сеть нестабильна.
  */

  av_dict_set(&options, "rtsp_transport", "tcp", 0); // UDP обычно дает меньшую задержку, хотя и менее надежен (важно для низкой задержки)
  av_dict_set(&options, "max_delay", "300000", 0); // максимальная задержка 100 мс (меньше 1)
  av_dict_set(&options, "buffer_size", "65536", 0); // размер буфера 64 Кб
  av_dict_set(&options, "probesize", "5000000", 0); // 5 Мб (уменьшено для быстрого определения потока)
  av_dict_set(&options, "analyzeduration", "500000", 0); // 1 секунда анализа (лучше уменьшить для быстрой инициализации)
  av_dict_set(&options, "fflags", "nobuffer", 0); // отключить внутренние буферы
  av_dict_set(&options, "flags", "low_delay", 0); // включить режим низкой задержки в декодере
  av_dict_set(&options, "tune", "zerolatency", 0); // оптимизация кодека на минимальную задержку (применимо к энкодеру, иногда полезно для декодера)
  av_dict_set(&options, "rtt_timeout", "3000000", 0); // таймаут RTSP 3 секунда (зависит от источника)
  // av_dict_set(&options, "timeout", "5000000", 0);            // общий таймаут в микросекундах (5 секунда)
  av_dict_set(&options, "pkt_size", "1316", 0); // UDP пакет размер (можно подогнать под MTU)
  av_dict_set(&options, "stimeout", "5000000", 0); // timeout для сокета (обязательно)
  // av_dict_set(&options, "fflags", "genpts", 0);                    // генерируем таймштамы, убираем nobuffer

  // Чтобы FFmpeg мог собирать пакеты, установите большее значение:
  av_dict_set(&options, "max_delay", "700000", 0); // 700 мс или даже 1с (1000000)

  uint64_t START = GLOBAL_TIME_START();
  if (avformat_open_input(fmt_ctx, url, NULL, &options) < 0) {
    fprintf(stderr, "Failed to open input stream: %s\n", url);

    printf("%s: длительность: %s %.3f\n", GLOBAL_TIME_PRINT(), GLOBAL_TIME_END_PRNT(START), GLOBAL_TIME_END(START));

    av_dict_free(&options);
    return -1;
  }
  av_dict_free(&options);
  printf("1 %s: длительность: %s %.3f\n", GLOBAL_TIME_PRINT(), GLOBAL_TIME_END_PRNT(START), GLOBAL_TIME_END(START));

  if (avformat_find_stream_info(*fmt_ctx, NULL) < 0) {
    fprintf(stderr, "Failed to find stream info\n");
    avformat_close_input(fmt_ctx);
    return -1;
  }
  printf("2 (int)(*fmt_ctx)->nb_streams: %d\n", (int)(*fmt_ctx)->nb_streams);

  *video_stream_idx = -1;

  enum AVCodecID codec_id = AV_CODEC_ID_NONE;

  AVCodecParameters* codecpar = NULL;
  for (int i = 0; i < (int)(*fmt_ctx)->nb_streams; i++) {
    if (!(*fmt_ctx)->streams[i]) {
      printf("Stream %d is NULL\n", i);
      continue;
    }
    if (!(*fmt_ctx)->streams[i]->codecpar) {
      printf("Stream %d codecpar is NULL\n", i);
      continue;
    }
    if ((*fmt_ctx)->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
      codecpar = (*fmt_ctx)->streams[i]->codecpar;

      printf("bit_rate %lld bit %d delay %d\r\n", codecpar->bit_rate, codecpar->bits_per_coded_sample, codecpar->video_delay);
      if (codecpar->codec_id == AV_CODEC_ID_H264 || codecpar->codec_id == AV_CODEC_ID_HEVC) {
        *video_stream_idx = i;
        codec_id = codecpar->codec_id;
        break;
      }
    }
  }

  printf("3 %s: длительность: %s %.3f\n", GLOBAL_TIME_PRINT(), GLOBAL_TIME_END_PRNT(START), GLOBAL_TIME_END(START));

  if (*video_stream_idx == -1) {
    fprintf(stderr, "Video stream not found (H264 or H265)\n");
    return -1;
  }

  printf("4 Stream resolution: %dx%d\n", codecpar->width, codecpar->height);
  /*
          if (codecpar->width > IMG_WIDTH || codecpar->height > IMG_HEIGHT) {
                  fprintf(stderr, "Resolution %dx%d exceeds max allowed %dx%d\n", codecpar->width, codecpar->height, IMG_WIDTH, IMG_HEIGHT);
                  return -1;
                  // здесь можно либо выйти с ошибкой, либо задать размеры для ресайза
          }

          AVDictionaryEntry  *entry = NULL;
          while ((entry = av_dict_get((*fmt_ctx)->metadata, "", entry, AV_DICT_IGNORE_SUFFIX))) {
                  printf("metadata: %s=%s\n", entry->key, entry->value);
                  if (strcmp(entry->key, "videoinfo") == 0) {
                          // парсим entry->value здесь
                  }
          }

          printf("5 Video stream bitrate: %" PRId64 " bps\n", bit_rate);
  */
  const AVCodec* codec = avcodec_find_decoder(codec_id);
  if (!codec) {
    fprintf(stderr, "Decoder for codec_id %d not found\n", codec_id);
    return -1;
  }

  *codec_ctx = avcodec_alloc_context3(codec);
  if (!*codec_ctx) {
    fprintf(stderr, "Failed to alloc codec context\n");
    return -1;
  }
  if (avcodec_parameters_to_context(*codec_ctx, (*fmt_ctx)->streams[*video_stream_idx]->codecpar) < 0) {
    fprintf(stderr, "Failed to copy codec params\n");
    return -1;
  }

  AVDictionary* opts = NULL;
  av_dict_set(&opts, "threads", "1", 0);
  av_dict_set(&opts, "low_delay", "1", 0);
  av_dict_set(&opts, "refcounted_frames", "1", 0);
  av_dict_set(&opts, "skip_idct", "all", 0);
  av_dict_set(&opts, "skip_loop_filter", "all", 0); // или "nonref"

  (*codec_ctx)->flags |= AV_CODEC_FLAG_LOW_DELAY;
  (*codec_ctx)->flags2 |= AV_CODEC_FLAG2_FAST | AV_CODEC_FLAG2_CHUNKS;

  // пропускать не референтные кадры
  //(*codec_ctx)->skip_frame = AVDISCARD_NONREF ;
  // пропускать не ключевые кадры
  //(*codec_ctx)->skip_frame = AVDISCARD_NONKEY;
  (*codec_ctx)->thread_count = 2;
  (*codec_ctx)->thread_type = 0;

  // Отключить наложенный вывод 16-битных кадров, если уверены, что не нужны:
  (*codec_ctx)->flags2 |= AV_CODEC_FLAG2_NO_OUTPUT;

  if (avcodec_open2(*codec_ctx, codec, &opts) < 0) {
    fprintf(stderr, "Failed to open codec\n");
    av_dict_free(&opts);
    return -1;
  }
  av_dict_free(&opts);

  // Корректный расчет dst_height, чтобы сохранить пропорции согласно IMG_WIDTH
  int dst_height = (int)((double)codecpar->height * IMG_WIDTH / codecpar->width);

  *frame = av_frame_alloc();
  *rgb_frame = av_frame_alloc();
  *pkt = av_packet_alloc();
  if (!*frame || !*rgb_frame || !*pkt) {
    fprintf(stderr, "Failed to allocate frame or packet\n");
    return -1;
  }

  // SWS_POINT SWS_FAST_BILINEAR
  *sws_ctx = sws_getContext((*codec_ctx)->width, (*codec_ctx)->height, (*codec_ctx)->pix_fmt, IMG_WIDTH, dst_height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
  if (!*sws_ctx) {
    fprintf(stderr, "Failed to initialize sws context\n");
    return -1;
  }

  (*rgb_frame)->format = AV_PIX_FMT_RGB24;
  (*rgb_frame)->width = IMG_WIDTH;
  (*rgb_frame)->height = IMG_HEIGHT; 

  if (av_frame_get_buffer(*rgb_frame, 0) < 0) {
    fprintf(stderr, "Failed to allocate buffer for rgb frame\n");
    return -1;
  }

  return 0;
}

void* camera_thread(void* arg)
{
  camera_thread_args_t* args = (camera_thread_args_t*)arg;

  AVFormatContext* fmt_ctx = NULL;
  AVCodecContext* codec_ctx = NULL;
  struct SwsContext* sws_ctx = NULL;
  AVFrame* frame = NULL;
  AVFrame* rgb_frame = NULL;
  AVPacket* pkt = NULL;
  int video_stream_idx = -1;

  int reconnect_delay_secs = 3;

  while (args->task->run) {

    if (camera_open_stream(args->url, &fmt_ctx, &video_stream_idx, &codec_ctx, &sws_ctx, &frame, &rgb_frame, &pkt) < 0) {
      fprintf(stderr, "Thread %d: Failed to open stream, retrying in %d seconds...\n", args->id, reconnect_delay_secs);
      sleep(reconnect_delay_secs);
      continue;
    }

    int frame_count = 0;
    int work_frame = 0;
    int FRAME_ROUND = 1;

    int cam_work = 1;

    while (cam_work == 1) {
      if (args->task->run != 1) {
        break;
      }

      int ret = av_read_frame(fmt_ctx, pkt);

      if (ret >= 0) {
				/*
        work_frame = 1;

        if (pkt->flags & AV_PKT_FLAG_KEY) {
          if (frame_count % FRAME_ROUND == 0) {
            work_frame = 1;
          }
        }

        if (work_frame == 1) {
					*/
					
          if (pkt->stream_index == video_stream_idx) {
            if (avcodec_send_packet(codec_ctx, pkt) == 0) {
              while (avcodec_receive_frame(codec_ctx, frame) == 0) {

                // 20 милисекунд
                memset(rgb_frame->data[0], 0, rgb_frame->linesize[0] * IMG_HEIGHT); 
							
                sws_scale(sws_ctx, (const uint8_t* const*)frame->data, frame->linesize, 0, frame->height, rgb_frame->data, rgb_frame->linesize);

                add_frame_to_queue(args->task, args->id, rgb_frame);
                // printf("33 %s: длительность: %s %.3f, %d: queue size: %d\n", GLOBAL_TIME_PRINT(), GLOBAL_TIME_END_PRNT(START3), GLOBAL_TIME_END(START3), args->id, args->task->queue_size);
              }
            }
          }

        if (frame_count >= 60) {
          frame_count = 0;
        }
        av_packet_unref(pkt);
      } else {
        av_packet_unref(pkt);
        fprintf(stderr, "Thread %d: av_read_frame error or stream closed, reconnecting...\n", args->id);
        break; // выходим из while (чтобы переподключиться)
      }
    }

    // Очистка ресурсов перед повторным подключением
    cleanup_ffmpeg_resources(&fmt_ctx, &codec_ctx, &sws_ctx, &frame, &rgb_frame, &pkt);

    if (args->task->run) {
      fprintf(stderr, "Thread %d: Reconnecting in %d seconds...\n", args->id, reconnect_delay_secs);
      sleep(reconnect_delay_secs);
    }
  }

  // Конец — выполнять очистку повторно если нужно
  cleanup_ffmpeg_resources(&fmt_ctx, &codec_ctx, &sws_ctx, &frame, &rgb_frame, &pkt);

  pthread_exit(NULL);
}

int main()
{
  avformat_network_init();
  av_log_set_level(AV_LOG_QUIET);
  // av_log_set_level(AV_LOG_DEBUG);

  // RKNN в отдельный поток
  rknn_task_t* task = malloc(sizeof(rknn_task_t));

  init_rknn_task(task);

  pthread_t rknn_thread;
  pthread_create(&rknn_thread, NULL, _rknn_thread, (void*)task);
  pthread_detach(rknn_thread);
  //--
  sleep(3);
  camera_thread_args_t args1 = { 
	//.url = "rtsp://192.168.1.11/live/0/MAIN",
    .url = "rtsp://admin:admin@192.168.1.91:554/2",
    .task = task,
    .id = 1
  };
  /*
      camera_thread_args_t args2 = {
          //.url = "rtsp://192.168.1.11/live/0/MAIN",
                  .url = "rtsp://admin:admin@192.168.1.92:554/2",
          .task = task,
          .id = 2
      };
  */
  pthread_t thread1;
  // pthread_t thread2;
  pthread_create(&thread1, NULL, camera_thread, &args1);
  // pthread_create(&thread2, NULL, camera_thread, &args2);

  // Ждём завершения потоков (если предусмотрено)
  pthread_join(thread1, NULL);
  // pthread_join(thread2, NULL);

  cleanup_rknn_task(task);
  free(task);

  avformat_network_deinit();

  return 0;
}

Правила:
Не удаляй комментарии, даже если они лишние или неактуальные.
Не переименовывай функции, переменные, константы и любые идентификаторы.
Не меняй структуру кода кардинально, если это не связано напрямую с исправлением ошибок.
Не добавляй новых функций или функционала — только исправления и улучшения существующего.
Пиши корректный, читаемый и оптимизированный код, если это необходимо для исправления.
При каждом изменении объясняй, что и почему исправил(а).
Предоставленный код возвращай в полном виде, без твоих комментариев
Если видишь что написан полный бред - предложи в отдельном - блоке комментарии

ОСНОВНОЕ что сейчас необходимо:
Оптимизировать по максимомму все вычисления, т.к это выполняется на 1 ЦПУ очень слабом.. памяти достаточно.. но вычисления медленные