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);
    }
    if (*rgb_frame) {
        av_frame_free(rgb_frame);
    }
    if (*pkt) {
        av_packet_free(pkt);
    }
    if (*sws_ctx) {
        sws_freeContext(*sws_ctx);
        *sws_ctx = NULL;
    }
    if (*codec_ctx) {
        avcodec_free_context(codec_ctx);
    }
    if (*fmt_ctx) {
        avformat_close_input(fmt_ctx);
    }
}

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;

		av_dict_set(&options, "max_delay", "10", 0);                 // минимальная задержка
		av_dict_set(&options, "rtsp_transport", "tcp", 0);           // TCP для надежности (зависит от сети, можно udp)
		av_dict_set(&options, "stimeout", "500000", 0);              // timeout 0.5 сек (500000 мкс)
		av_dict_set(&options, "buffer_size", "32768", 0);            // сократить буфер до 32 Кб
		av_dict_set(&options, "probesize", "30000", 0);              // уменьшить анализ с 50 до 30 Кб
		av_dict_set(&options, "analyzeduration", "300000", 0);       // уменьшить анализ с 0.5до   0.3 сек
		//av_dict_set(&options, "fflags", "nobuffer", 0);              // отключить буфер
		av_dict_set(&options, "fflags", "genpts", 0);                    // генерируем таймштамы, убираем nobuffer
		av_dict_set(&options, "flags", "low_delay", 0);              // режим низкой задержки
		av_dict_set(&options, "tune", "zerolatency", 0);             // оптимизация нулевой задержки

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

    if (avformat_find_stream_info(*fmt_ctx, NULL) < 0) {
        fprintf(stderr, "Failed to find stream info\n");
        return -1;
    }

		*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]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
						codecpar = (*fmt_ctx)->streams[i]->codecpar;
						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;
						}
				}
		}

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

		printf("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;
				// здесь можно либо выйти с ошибкой, либо задать размеры для ресайза
		}
	
		int64_t bit_rate = codecpar->bit_rate;
		printf("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);

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

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

    *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;
    }

    (*rgb_frame)->format = AV_PIX_FMT_RGB24;
    (*rgb_frame)->width = (*codec_ctx)->width;
    (*rgb_frame)->height = (*codec_ctx)->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 width_ok = codec_ctx->width;
				int height_ok = codec_ctx->height;
				int cam_work = 1;
				
				int64_t start_time = av_gettime();  // время в микросекундах

				int frame_fps = 0;
				const int interval_usec = 1 * 1000000;  // 2 секунды в микросекундах

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

            int ret = av_read_frame(fmt_ctx, pkt);
            if (ret >= 0) {
                frame_count++;
                work_frame = 0;
                if (pkt->flags & AV_PKT_FLAG_KEY) work_frame = 1;
                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) {
															
																if(width_ok != rgb_frame->width || height_ok != rgb_frame->height) {
																	cam_work = 0;
																	break;
																}
                                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, rgb_frame);

                                printf("Thread %d: Frame processed; queue size: %d\n", args->id, args->task->queue_size);
																 frame_fps++;

																int64_t now = av_gettime();
																if (now - start_time >= interval_usec) {
																		double seconds = (now - start_time) / 1000000.0;
																		double fps = frame_fps / seconds;

																		printf("Frames in last %.2f seconds: %d, approx FPS: %.2f\n", seconds, frame_fps, fps);

																		// Обнуляем счётчики для следующего периода
																		frame_fps = 0;
																		start_time = now;
																}
                            }
                        }
                    }
                }
                if (frame_count >= 3600) {
									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);
}


Не видит поток h264