FFmpeg基础:SDL渲染显示视频流数据

时间:2025-03-09 09:39:51
#include <> #include <> #include <libavcodec/> #include <libavformat/> #include <libswscale/> #include <libswresample/> #include <libavutil/> #include <> //@1视频文件的地址 int main(int argc, char *argv[]) { int ret = -1; int quit = 0; int videoStream; AVFormatContext *pFormatCtx = NULL; //视频解码上下文,解码器 AVCodecContext *pSourceCodecCtx = NULL; AVCodec *pSourceCodec = NULL; //图像转换上下文 struct SwsContext *sws_ctx = NULL; //图像数据格式 uint8_t *dst_data[4] = { 0 }; int dst_linesize[4] = { 0 }; AVFrame *pFrame = NULL; AVPacket packet; //视频宽高 int w_width = 640; int w_height = 480; //SDL渲染用 int pixformat; SDL_Rect rect; SDL_Window *win; SDL_Renderer *renderer; SDL_Texture *texture; SDL_Event event; SDL_AudioSpec wanted_spec, spec; if (argc < 2) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Usage: command <file>"); return ret; } if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not initialize SDL - %s\n", SDL_GetError()); return ret; } //打开视频上下文 if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open multi-media file"); goto __FAIL; // Couldn't open file } //获取流信息信息 if (avformat_find_stream_info(pFormatCtx, NULL)<0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't find stream information "); goto __FAIL; } //查找视频流 videoStream = -1; for (int i = 0; i<pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && videoStream < 0) { videoStream = i; } } if (videoStream == -1) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, " Didn't find a video stream "); goto __FAIL; } //查找视频流的解码器 pSourceCodec = avcodec_find_decoder(pFormatCtx->streams[videoStream]->codecpar->codec_id); if (pSourceCodec == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unsupported codec!"); goto __FAIL; } //创建解码器的上下文,并拷贝参数 pSourceCodecCtx = avcodec_alloc_context3(pSourceCodec); ret = avcodec_parameters_to_context(pSourceCodecCtx, pFormatCtx->streams[videoStream]->codecpar); if (ret < 0) { printf("Failed to copy in_stream codecpar to codec context\n"); goto __FAIL; } //打开解码器 if (avcodec_open2(pSourceCodecCtx, pSourceCodec, NULL)<0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open audio decoder!"); goto __FAIL; } pFrame = av_frame_alloc(); w_width = pSourceCodecCtx->width; w_height = pSourceCodecCtx->height; //创建输出窗口 win = SDL_CreateWindow("SDL Video Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w_width, w_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!win) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create window!"); goto __FAIL; } //SDL渲染器 renderer = SDL_CreateRenderer(win, -1, 0); if (!renderer) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create renderer!"); goto __FAIL; } //创建显示帧 pixformat = SDL_PIXELFORMAT_IYUV; texture = SDL_CreateTexture(renderer, pixformat, SDL_TEXTUREACCESS_STREAMING, w_width, w_height); if (!texture) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Texture!"); goto __FAIL; } //转换图像格式 sws_ctx = sws_getContext(pSourceCodecCtx->width, pSourceCodecCtx->height, pSourceCodecCtx->pix_fmt, pSourceCodecCtx->width, pSourceCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); ret = av_image_alloc( dst_data, dst_linesize, pSourceCodecCtx->width, pSourceCodecCtx->height, AV_PIX_FMT_YUV420P, 1); //读取数据帧并渲染 while (av_read_frame(pFormatCtx, &packet) >= 0) { if (packet.stream_index == videoStream) { ret = avcodec_send_packet(pSourceCodecCtx, &packet); if (ret < 0) { continue; } ret = avcodec_receive_frame(pSourceCodecCtx, pFrame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { ret = 0; continue; } if (ret < 0) { continue; } //对图像进行缩放 sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pSourceCodecCtx->height, dst_data, dst_linesize); //渲染图像 SDL_UpdateYUVTexture(texture, NULL, dst_data[0], dst_linesize[0], dst_data[1], dst_linesize[1], dst_data[2], dst_linesize[2]); rect.x = 0; rect.y = 0; rect.w = pSourceCodecCtx->width; rect.h = pSourceCodecCtx->height; SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, &rect); SDL_RenderPresent(renderer); SDL_Delay(1000 / 30); av_packet_unref(&packet); } //处理SDL事件 SDL_PollEvent(&event); } __FAIL: //清理分配的数据 av_freep(&dst_data[0]); if (pFormatCtx) { avformat_close_input(&pFormatCtx); } if (pFrame) { av_frame_free(&pFrame); } if (pSourceCodecCtx) { avcodec_close(pSourceCodecCtx); } if (pSourceCodecCtx) { avcodec_close(pSourceCodecCtx); } if (pFormatCtx) { avformat_close_input(&pFormatCtx); } if (win) { SDL_DestroyWindow(win); } if (renderer) { SDL_DestroyRenderer(renderer); } if (texture) { SDL_DestroyTexture(texture); } SDL_Quit(); return ret; }