21xrx.com
2024-05-20 03:41:33 Monday
登录
文章检索 我的文章 写文章
用FFmpeg和SDL打开摄像头
2023-11-13 15:02:15 深夜i     --     --
FFmpeg SDL 摄像头 打开

FFmpeg和SDL是既功能强大又广泛使用的开源库,它们提供了许多功能,其中之一就是打开摄像头并显示摄像头数据。本文将介绍如何使用FFmpeg和SDL打开摄像头。

首先,我们需要安装FFmpeg和SDL库。在Linux系统上,可以使用包管理器来安装它们。在Ubuntu上,可以运行以下命令来安装FFmpeg和SDL:


sudo apt-get install ffmpeg libsdl2-dev

接下来,我们需要编写一个简单的程序来打开摄像头。首先,我们需要包含FFmpeg和SDL的头文件:


#include <stdio.h>

#include <SDL2/SDL.h>

#include <libavformat/avformat.h>

#include <libswscale/swscale.h>

然后,我们需要定义一些全局变量来存储摄像头数据:


SDL_Window *window;

SDL_Renderer *renderer;

SDL_Texture *texture;

SDL_Rect rect;

AVFormatContext *formatCtx;

AVCodecContext *codecCtx;

AVCodec *codec;

AVFrame *frame;

AVPacket packet;

struct SwsContext *swsCtx;

int videoStreamIndex;

int width, height;

接下来,我们需要实现一个函数来打开摄像头和SDL窗口,并初始化相关变量:


int init() {

  av_register_all();

  formatCtx = avformat_alloc_context();

  if (avformat_open_input(&formatCtx, "/dev/video0", NULL, NULL) != 0) {

    printf("Could not open camera\n");

    return -1;

  }

  if (avformat_find_stream_info(formatCtx, NULL) < 0) {

    printf("Failed to retrieve input stream information\n");

    return -1;

  }

  videoStreamIndex = -1;

  for (int i = 0; i < formatCtx->nb_streams; i++) {

    if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)

      videoStreamIndex = i;

      break;

    

  }

  if (videoStreamIndex == -1) {

    printf("Could not find any video stream\n");

    return -1;

  }

  codecCtx = avcodec_alloc_context3(NULL);

  avcodec_parameters_to_context(codecCtx, formatCtx->streams[videoStreamIndex]->codecpar);

  codec = avcodec_find_decoder(codecCtx->codec_id);

  if (codec == NULL) {

    printf("Codec not found\n");

    return -1;

  }

  if (avcodec_open2(codecCtx, codec, NULL) < 0) {

    printf("Could not open codec\n");

    return -1;

  }

  frame = av_frame_alloc();

  width = codecCtx->width;

  height = codecCtx->height;

  window = SDL_CreateWindow("Camera", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);

  if (window == NULL) {

    printf("Could not create window\n");

    return -1;

  }

  renderer = SDL_CreateRenderer(window, -1, 0);

  if (renderer == NULL) {

    printf("Could not create renderer\n");

    return -1;

  }

  texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, width, height);

  if (texture == NULL) {

    printf("Could not create texture\n");

    return -1;

  }

  rect.x = 0;

  rect.y = 0;

  rect.w = width;

  rect.h = height;

  swsCtx = sws_getContext(width, height, codecCtx->pix_fmt, width, height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);

  if (swsCtx == NULL) {

    printf("Could not create sws context\n");

    return -1;

  }

  return 0;

}

接下来,我们需要编写一个函数来循环读取摄像头数据,并将其渲染到SDL窗口中:


void render() {

  while (av_read_frame(formatCtx, &packet) >= 0) {

    if (packet.stream_index == videoStreamIndex) {

      avcodec_send_packet(codecCtx, &packet);

      while (avcodec_receive_frame(codecCtx, frame) == 0) {

        sws_scale(swsCtx, frame->data, frame->linesize, 0, height, frame->data, frame->linesize);

        SDL_UpdateYUVTexture(texture, &rect, frame->data[0], frame->linesize[0], frame->data[1], frame->linesize[1], frame->data[2], frame->linesize[2]);

        SDL_RenderClear(renderer);

        SDL_RenderCopy(renderer, texture, NULL, &rect);

        SDL_RenderPresent(renderer);

      }

    }

    av_packet_unref(&packet);

  }

}

最后,我们需要编写一个函数来释放所有资源:


void cleanup() {

  avformat_close_input(&formatCtx);

  av_frame_free(&frame);

  avcodec_close(codecCtx);

  sws_freeContext(swsCtx);

  SDL_DestroyTexture(texture);

  SDL_DestroyRenderer(renderer);

  SDL_DestroyWindow(window);

  SDL_Quit();

}

现在,我们可以在`main`函数中调用这些函数来打开摄像头和显示摄像头数据:


int main() {

  if (init() == 0) {

    render();

    cleanup();

  }

  return 0;

}

通过以上步骤,我们成功地使用FFmpeg和SDL来打开摄像头并显示摄像头数据。这为开发各种摄像头应用程序提供了一个良好的基础。你也可以根据需要对代码进行优化和扩展,以满足特定的需求。使用这一强大的组合,你可以轻松地处理摄像头数据,并将其集成到自己的项目中。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复