Qt,ffmpeg,SDL2实现一个简单的视频播放器

发布时间 2023-09-13 15:02:06作者: 人间别久不成悲

先贴一下这三个库的版本:
QT:6.2.4

fmpeg:5.1.3

SDL2

总体思路是参考的雷神的博客:最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)_flush decoder_雷霄骅的博客-CSDN博客

  1 #include "videopanel.h"
  2 #include "ui_VideoPanel.h"
  3 
  4 
  5 VideoPanel::VideoPanel(QWidget *parent) :
  6         QWidget(parent), ui(new Ui::VideoPanel) {
  7     ui->setupUi(this);
  8 
  9 }
 10 
 11 VideoPanel::~VideoPanel() {
 12     SDL_Quit();
 13     av_frame_free(&frame);
 14     av_frame_free(&yuvFrame);
 15     avcodec_close(codecContext);
 16     avformat_close_input(&formatContext);
 17     delete ui;
 18 }
 19 
 20 void VideoPanel::initAV() {
 21     avformat_network_init();
 22     formatContext = avformat_alloc_context();
 23     frame = av_frame_alloc();
 24     yuvFrame = av_frame_alloc();
 25     if(pt.joinable()) pt.join();
 26 }
 27 
 28 int VideoPanel::openFile(const char *fileName) {
 29     initAV();
 30 
 31     if(avformat_open_input(&formatContext,fileName, nullptr, nullptr) != 0){
 32         printf("Couldn't open input stream.\n");
 33         return -1;
 34     }
 35 
 36     if(avformat_find_stream_info(formatContext, nullptr) <0 ){
 37         printf("Couldn't find stream information.\n");
 38         return -1;
 39     }
 40 
 41     videoIndex=-1;
 42     for(i=0; i<formatContext->nb_streams; i++){
 43         if(formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
 44             videoIndex=i;
 45             break;
 46         }
 47     }
 48 
 49     if(videoIndex==-1){
 50         printf("Didn't find a video stream.\n");
 51         return -1;
 52     }
 53 
 54     codecContext = avcodec_alloc_context3(nullptr);
 55     if(avcodec_parameters_to_context(codecContext,formatContext->streams[videoIndex]->codecpar) < 0){
 56         printf("Can't find codec\n");
 57     }
 58 
 59     codec = avcodec_find_decoder(codecContext->codec_id);
 60     codecContext->codec_id = codec->id;
 61     avcodec_open2(codecContext, codec, nullptr);
 62 
 63     avPacket=(AVPacket *)av_malloc(sizeof(AVPacket));
 64 
 65     img_convert_ctx = sws_getContext(codecContext->width,
 66                                      codecContext->height,
 67                                      codecContext->pix_fmt,
 68                                      codecContext->width,
 69                                      codecContext->height,
 70                                      AV_PIX_FMT_YUV420P,
 71                                      SWS_BICUBIC,
 72                                      nullptr,
 73                                      nullptr,
 74                                      nullptr);
 75 
 76     if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
 77         printf( "Could not initialize SDL - %s\n", SDL_GetError());
 78         return -1;
 79     }
 80 
 81     screen_width = codecContext->width;
 82     screen_height = codecContext->height;
 83 
 84     sdlWindow = SDL_CreateWindowFrom((void *)ui->label->winId());
 85 
 86     if(!sdlWindow) {
 87         printf("SDL: could not create window - exiting:%s\n",SDL_GetError());
 88         return -1;
 89     }
 90 
 91     sdlRenderer = SDL_CreateRenderer(sdlWindow, -1,SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
 92     if(!sdlRenderer){
 93         av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());
 94         sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0);
 95     }
 96 
 97     sdlTexture = SDL_CreateTexture(sdlRenderer,
 98                                    SDL_PIXELFORMAT_IYUV,
 99                                    SDL_TEXTUREACCESS_STREAMING,
100                                    codecContext->width,
101                                    codecContext->height);
102 
103     sdlRect.x = 0;
104     sdlRect.y = 0;
105     sdlRect.w = screen_width;
106     sdlRect.h = screen_height;
107 
108     yuvFrame->width = codecContext->width;
109     yuvFrame->height = codecContext->height;
110     yuvFrame->format = AV_PIX_FMT_YUV420P;
111     av_frame_get_buffer(yuvFrame,1);
112 
113     frame_time = 1000.0 / av_q2d(formatContext->streams[videoIndex]->r_frame_rate);
114 
115     pt = std::thread(&VideoPanel::playThread, this);
116     return 0;
117 }
118 
119 void VideoPanel::playThread() {
120     while(av_read_frame(formatContext, avPacket) >= 0){
121         if(avPacket->stream_index == videoIndex){
122             if ((ret = avcodec_send_packet(codecContext, avPacket)) == AVERROR(EAGAIN)) {
123                 av_log(codecContext, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
124             }
125             else {
126                 av_packet_unref(avPacket);
127             }
128 
129             while (avcodec_receive_frame(codecContext, frame) == 0){
130                 sws_scale(img_convert_ctx,
131                           (const uint8_t * const*)frame->data,
132                           frame->linesize,
133                           0,
134                           codecContext->height,
135                           yuvFrame->data,
136                           yuvFrame->linesize);
137 
138                 SDL_UpdateYUVTexture(sdlTexture,
139                                      &sdlRect,
140                                      yuvFrame->data[0],
141                                      yuvFrame->linesize[0],
142                                      yuvFrame->data[1],
143                                      yuvFrame->linesize[1],
144                                      yuvFrame->data[2],
145                                      yuvFrame->linesize[2]);
146 
147                 SDL_RenderClear(sdlRenderer);
148                 SDL_RenderCopy(sdlRenderer, sdlTexture, nullptr, &sdlRect);
149                 SDL_RenderPresent(sdlRenderer);
150                 SDL_Delay(40);
151             }
152         }
153         av_packet_unref(avPacket);
154     }
155     sws_freeContext(img_convert_ctx);
156 }