From d8a92b0d4f203d6e793d735a8067d3b55e5fa1ce Mon Sep 17 00:00:00 2001 From: Mike Mi <2873@qq.com> Date: Tue, 31 Mar 2026 10:58:56 +0800 Subject: [PATCH] version1 --- v1_camera_pipeline_ifdef.c | 454 +++++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 v1_camera_pipeline_ifdef.c diff --git a/v1_camera_pipeline_ifdef.c b/v1_camera_pipeline_ifdef.c new file mode 100644 index 0000000..7c02cfc --- /dev/null +++ b/v1_camera_pipeline_ifdef.c @@ -0,0 +1,454 @@ +// camera_pipeline_ifdef_fixed.c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// FFmpeg头文件 - 使用纯C包含 +#include +#include +#include +#include +#include +#include + +// ========================================== +// 1. 配置区域:在这里开启或关闭时间打印 +// ========================================== +#define DEBUG_TIMING // 注释掉这一行,所有时间打印和计算都会消失 + +// 定义打印宏 +#ifdef DEBUG_TIMING + #define PRINT_TIME(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else + #define PRINT_TIME(fmt, ...) // 什么都不做,编译器会优化掉 +#endif + +#define WIDTH 1280 +#define HEIGHT 720 +#define OUTPUT_WIDTH 640 +#define OUTPUT_HEIGHT 360 +#define NUM_BUFFERS 4 +#define CLEAR(x) memset(&(x), 0, sizeof(x)) + +typedef struct { + void *start; + size_t length; +} Buffer; + +double get_time_ms() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000.0 + ts.tv_nsec / 1000000.0; +} + +int open_v4l2_device(const char* device) { + int fd = open(device, O_RDWR | O_NONBLOCK); + if (fd < 0) { + perror("open device"); + return -1; + } + return fd; +} + +int init_v4l2_device(int fd) { + struct v4l2_format fmt = {0}; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = WIDTH; + fmt.fmt.pix.height = HEIGHT; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; + fmt.fmt.pix.field = V4L2_FIELD_NONE; + + if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { + perror("VIDIOC_S_FMT"); + return -1; + } + + PRINT_TIME("Set format: %dx%d MJPEG\n", WIDTH, HEIGHT); + return 0; +} + +int init_mmap(int fd, Buffer** buffers, int* num_buffers) { + struct v4l2_requestbuffers req = {0}; + req.count = NUM_BUFFERS; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + + if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) { + perror("VIDIOC_REQBUFS"); + return -1; + } + + *num_buffers = req.count; + *buffers = (Buffer*)calloc(req.count, sizeof(Buffer)); + + for (int i = 0; i < req.count; i++) { + struct v4l2_buffer buf = {0}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { + perror("VIDIOC_QUERYBUF"); + return -1; + } + + (*buffers)[i].length = buf.length; + (*buffers)[i].start = mmap(NULL, buf.length, + PROT_READ | PROT_WRITE, + MAP_SHARED, fd, buf.m.offset); + + if ((*buffers)[i].start == MAP_FAILED) { + perror("mmap"); + return -1; + } + } + + return 0; +} + +AVCodecContext* create_mjpeg_decoder() { + const AVCodec* decoder = avcodec_find_decoder(AV_CODEC_ID_MJPEG); + if (!decoder) { + fprintf(stderr, "MJPEG decoder not found\n"); + return NULL; + } + + AVCodecContext* ctx = avcodec_alloc_context3(decoder); + ctx->width = WIDTH; + ctx->height = HEIGHT; + ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; // 使用YUVJ420P + ctx->color_range = AVCOL_RANGE_JPEG; // JPEG范围 + ctx->thread_count = 1; + + AVDictionary* opts = NULL; + av_dict_set(&opts, "flags2", "+fast", 0); + + if (avcodec_open2(ctx, decoder, &opts) < 0) { + avcodec_free_context(&ctx); + av_dict_free(&opts); + return NULL; + } + + av_dict_free(&opts); + printf("Decoder created with format: YUVJ420P\n"); + return ctx; +} + +AVCodecContext* create_mjpeg_encoder() { + const AVCodec* encoder = avcodec_find_encoder(AV_CODEC_ID_MJPEG); + if (!encoder) { + fprintf(stderr, "MJPEG encoder not found\n"); + return NULL; + } + + AVCodecContext* ctx = avcodec_alloc_context3(encoder); + ctx->width = OUTPUT_WIDTH; + ctx->height = OUTPUT_HEIGHT; + ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; // 使用YUVJ420P + ctx->time_base = (AVRational){1, 30}; + ctx->qmin = 8; // 量化参数控制,8大小为10-15kb,2 大小为20-35KB + ctx->qmax = 31; + ctx->flags |= AV_CODEC_FLAG_QSCALE; + ctx->global_quality = FF_QP2LAMBDA * 5; + + AVDictionary* opts = NULL; + av_dict_set(&opts, "huffman", "default", 0); + + if (avcodec_open2(ctx, encoder, &opts) < 0) { + avcodec_free_context(&ctx); + av_dict_free(&opts); + return NULL; + } + + av_dict_free(&opts); + printf("Encoder created with format: YUVJ420P\n"); + return ctx; +} + +int decode_mjpeg_frame(AVCodecContext* decoder, const uint8_t* data, int size, AVFrame** frame) { + AVPacket* pkt = av_packet_alloc(); + pkt->data = (uint8_t*)data; + pkt->size = size; + + int ret = avcodec_send_packet(decoder, pkt); + if (ret < 0) { + av_packet_free(&pkt); + return -1; + } + + *frame = av_frame_alloc(); + ret = avcodec_receive_frame(decoder, *frame); + + av_packet_free(&pkt); + return ret; +} + +int scale_frame(AVFrame* src, AVFrame** dst) { + // 简单缩放,不设置复杂的色彩空间参数 + struct SwsContext* sws_ctx = sws_getContext( + src->width, src->height, src->format, + OUTPUT_WIDTH, OUTPUT_HEIGHT, AV_PIX_FMT_YUVJ420P, + SWS_BILINEAR, NULL, NULL, NULL); + + if (!sws_ctx) { + fprintf(stderr, "Failed to create sws context\n"); + return -1; + } + + *dst = av_frame_alloc(); + if (!*dst) { + sws_freeContext(sws_ctx); + return -1; + } + + (*dst)->width = OUTPUT_WIDTH; + (*dst)->height = OUTPUT_HEIGHT; + (*dst)->format = AV_PIX_FMT_YUVJ420P; + + if (av_frame_get_buffer(*dst, 0) < 0) { + fprintf(stderr, "Failed to allocate frame buffer\n"); + av_frame_free(dst); + sws_freeContext(sws_ctx); + return -1; + } + + int ret = sws_scale(sws_ctx, (const uint8_t* const*)src->data, src->linesize, + 0, src->height, (*dst)->data, (*dst)->linesize); + + sws_freeContext(sws_ctx); + + if (ret < 0) { + fprintf(stderr, "sws_scale failed\n"); + av_frame_free(dst); + return -1; + } + + return 0; +} + +int encode_frame(AVCodecContext* encoder, AVFrame* frame, AVPacket** pkt) { + *pkt = av_packet_alloc(); + int ret = avcodec_send_frame(encoder, frame); + if (ret < 0) return -1; + + ret = avcodec_receive_packet(encoder, *pkt); + return ret; +} + +int main() { + PRINT_TIME("=== V4L2 Direct Capture + FFmpeg Processing ===\n"); + + // 1. Open V4L2 device + int fd = open_v4l2_device("/dev/video0"); + if (fd < 0) { + fprintf(stderr, "Failed to open camera device\n"); + return 1; + } + + // 2. Initialize V4L2 + if (init_v4l2_device(fd) < 0) { + close(fd); + return 1; + } + + // 3. Setup MMAP buffers + Buffer* buffers = NULL; + int num_buffers = 0; + if (init_mmap(fd, &buffers, &num_buffers) < 0) { + close(fd); + return 1; + } + + // 4. Create FFmpeg codecs + AVCodecContext* decoder = create_mjpeg_decoder(); + AVCodecContext* encoder = create_mjpeg_encoder(); + if (!decoder || !encoder) { + fprintf(stderr, "Failed to create codecs\n"); + close(fd); + return 1; + } + + // 5. Queue buffers + for (int i = 0; i < num_buffers; i++) { + struct v4l2_buffer buf = {0}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { + perror("VIDIOC_QBUF"); + close(fd); + return 1; + } + } + + // 6. Start streaming + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) { + perror("VIDIOC_STREAMON"); + close(fd); + return 1; + } + + // 7. Benchmark + // 使用宏控制打印表头 + PRINT_TIME("\nRunning benchmark (100 frames)...\n"); + PRINT_TIME("Frame | Capture | Decode | Scale | Encode | Total | Size | Marker\n"); + PRINT_TIME("------|---------|--------|-------|--------|-------|------|--------\n"); + + for (int i = 0; i < 100; i++) { + // 只有在开启 DEBUG_TIMING 时才声明这些时间变量 + #ifdef DEBUG_TIMING + double total_start = get_time_ms(); + double capture_start, capture_end; + double decode_start, decode_end; + double scale_start, scale_end; + double encode_start, encode_end; + #endif + + // Wait for frame + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + + struct timeval tv = {2, 0}; + int r = select(fd + 1, &fds, NULL, NULL, &tv); + if (r <= 0) { + PRINT_TIME("Timeout waiting for frame\n"); + break; + } + + #ifdef DEBUG_TIMING + capture_start = get_time_ms(); + #endif + + // Dequeue buffer + struct v4l2_buffer buf = {0}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) { + perror("VIDIOC_DQBUF"); + break; + } + + #ifdef DEBUG_TIMING + capture_end = get_time_ms(); + #endif + + // Decode + #ifdef DEBUG_TIMING + decode_start = get_time_ms(); + #endif + + AVFrame* decoded_frame = NULL; + int ret = decode_mjpeg_frame(decoder, + (uint8_t*)buffers[buf.index].start, buf.bytesused, &decoded_frame); + + #ifdef DEBUG_TIMING + decode_end = get_time_ms(); + #endif + + if (ret < 0 || !decoded_frame) { + PRINT_TIME("Frame %d: Decode failed\n", i + 1); + ioctl(fd, VIDIOC_QBUF, &buf); + continue; + } + + // Scale + #ifdef DEBUG_TIMING + scale_start = get_time_ms(); + #endif + + AVFrame* scaled_frame = NULL; + if (scale_frame(decoded_frame, &scaled_frame) < 0) { + PRINT_TIME("Frame %d: Scale failed\n", i + 1); + av_frame_free(&decoded_frame); + ioctl(fd, VIDIOC_QBUF, &buf); + continue; + } + + #ifdef DEBUG_TIMING + scale_end = get_time_ms(); + #endif + + // Encode + #ifdef DEBUG_TIMING + encode_start = get_time_ms(); + #endif + + AVPacket* encoded_pkt = NULL; + if (encode_frame(encoder, scaled_frame, &encoded_pkt) < 0) { + PRINT_TIME("Frame %d: Encode failed\n", i + 1); + } + #ifdef DEBUG_TIMING + if (encoded_pkt && i%50 == 0) { + char filename[100]; + sprintf(filename, "frame_%04d.jpg", i+1); + + FILE* f = fopen(filename, "wb"); + if (f) { + fwrite(encoded_pkt->data, 1, encoded_pkt->size, f); + fclose(f); + PRINT_TIME("Saved as %s\n", filename); + } } + #endif + + #ifdef DEBUG_TIMING + encode_end = get_time_ms(); + double total_end = get_time_ms(); + #endif + + // 打印结果 + #ifdef DEBUG_TIMING + PRINT_TIME("%5d | %7.1f | %6.1f | %5.1f | %6.1f | %5.1f | %4d KB | 0x%02x\n", + i + 1, + capture_end - capture_start, + decode_end - decode_start, + scale_end - scale_start, + encode_end - encode_start, + total_end - total_start, + encoded_pkt ? encoded_pkt->size / 1024 : 0, + encoded_pkt && encoded_pkt->size > 1 ? encoded_pkt->data[1] : 0 + ); + #else + // 如果不开启宏,也打印一些基本信息 + printf("Frame %d processed\n", i + 1); + #endif + + // Cleanup + av_frame_free(&decoded_frame); + av_frame_free(&scaled_frame); + if (encoded_pkt) av_packet_free(&encoded_pkt); + + // Requeue buffer + if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { + perror("VIDIOC_QBUF"); + break; + } + } + + // 8. Stop streaming + ioctl(fd, VIDIOC_STREAMOFF, &type); + + // 9. Cleanup + for (int i = 0; i < num_buffers; i++) { + if (buffers[i].start != MAP_FAILED) { + munmap(buffers[i].start, buffers[i].length); + } + } + free(buffers); + avcodec_free_context(&encoder); + avcodec_free_context(&decoder); + close(fd); + + return 0; +} +//gcc -o v1_camera_pipeline_ifdef v1_camera_pipeline_ifdef.c $(pkg-config --cflags --libs libavformat libavcodec libavutil libswscale)