注:这段代码存在问题,在点击关闭按钮时不能退出。后续添加了qttimer和qt线程的处理方式
    代码 videocapture.h

    1. #ifndef VIDEOCAPTURE_H
    2. #define VIDEOCAPTURE_H
    3. #include <iostream>
    4. #include <QWidget>
    5. #include <QLabel>
    6. #include <QPushButton>
    7. #include <QCloseEvent>
    8. extern "C"
    9. {
    10. #include <stdio.h>
    11. #include <libavcodec/avcodec.h>
    12. #include <libavformat/avformat.h>
    13. #include <libswscale/swscale.h>
    14. #include <libavdevice/avdevice.h>
    15. #include <libavformat/version.h>
    16. #include <libavutil/time.h>
    17. #include <libavutil/mathematics.h>
    18. //#include <libavutil/imgutils.h">
    19. #include <SDL2/SDL.h>
    20. }
    21. //event message
    22. #define REFRESH_EVENT (SDL_USEREVENT + 1)
    23. #define QUIT_EVENT (SDL_USEREVENT + 2)
    24. QT_BEGIN_NAMESPACE
    25. namespace Ui { class VideoCapture; }
    26. QT_END_NAMESPACE
    27. class VideoCapture : public QWidget
    28. {
    29. Q_OBJECT
    30. public:
    31. VideoCapture(QWidget *parent = nullptr);
    32. ~VideoCapture();
    33. void close_play();
    34. private:
    35. QLabel *label;
    36. Ui::VideoCapture *ui;
    37. QPushButton *start_btn;
    38. QRect rect;
    39. bool isplay;
    40. private slots:
    41. void start_play();
    42. protected:
    43. void closeEvent(QCloseEvent *event);
    44. };
    45. #endif // VIDEOCAPTURE_H

    代码 videocapture.cpp

    1. #include "videocapture.h"
    2. #include "ui_videocapture.h"
    3. #include <QDebug>
    4. #include <QTimer>
    5. VideoCapture::VideoCapture(QWidget *parent)
    6. : QWidget(parent)
    7. , ui(new Ui::VideoCapture)
    8. {
    9. ui->setupUi(this);
    10. qDebug() << "123";
    11. this->setFixedSize(1280,720);
    12. label = new QLabel(this);
    13. label->resize(1280,720);
    14. qDebug() << "456";
    15. label->move(0,0);
    16. rect = label->geometry();//记录widget位置,恢复时使用
    17. isplay = false;
    18. start_btn = new QPushButton("btn1" ,this);
    19. connect(start_btn,&QPushButton::clicked,this,&VideoCapture::start_play);
    20. connect(start_btn,&QPushButton::clicked,this,&VideoCapture::start_play);
    21. }
    22. VideoCapture::~VideoCapture()
    23. {
    24. delete ui;
    25. }
    26. static AVFrame* create_frame(int width, int height, AVPixelFormat pix_fmt)
    27. {
    28. int ret = 0;
    29. AVFrame* frame = NULL;
    30. frame = av_frame_alloc();
    31. if (!frame)
    32. {
    33. printf("Error, No Memory!\n");
    34. goto __ERROR;
    35. }
    36. //设置参数
    37. frame->width = width;
    38. frame->height = height;
    39. frame->format = pix_fmt;
    40. //alloc inner memory
    41. ret = av_frame_get_buffer(frame, 32); //按32位对齐 【视频必须是32位对齐】
    42. if (ret < 0)
    43. {
    44. printf("Error, Failed to alloc buffer for frame!\n");
    45. goto __ERROR;
    46. }
    47. return frame;
    48. __ERROR:
    49. if (frame)
    50. {
    51. av_frame_free(&frame);
    52. }
    53. return NULL;
    54. }
    55. int thread_exit = 0;
    56. int refresh_video_timer(void *udata)
    57. {
    58. thread_exit = 0;
    59. while (!thread_exit)
    60. {
    61. SDL_Event event;
    62. event.type = REFRESH_EVENT;
    63. SDL_PushEvent(&event);
    64. SDL_Delay(40);
    65. }
    66. thread_exit = 0;
    67. //push quit event
    68. SDL_Event event;
    69. event.type = QUIT_EVENT;
    70. SDL_PushEvent(&event);
    71. return 0;
    72. }
    73. void VideoCapture::close_play()
    74. {
    75. thread_exit = 1;
    76. SDL_Event event;
    77. event.type = QUIT_EVENT;
    78. SDL_PushEvent(&event);
    79. }
    80. void VideoCapture::closeEvent(QCloseEvent *event)
    81. {
    82. thread_exit = 1;
    83. isplay = 0;
    84. }
    85. void VideoCapture::start_play()
    86. {
    87. //使用ffmpeg -dshow采集视频
    88. int ret = 0;
    89. AVInputFormat *in_format = nullptr;
    90. AVFormatContext* fmt_ctx = nullptr;
    91. AVDictionary* options = nullptr;
    92. char device_name[256] = "video=Integrated Webcam";
    93. isplay = true;
    94. SDL_Rect sdlRect;
    95. //av_register_all();
    96. avdevice_register_all();
    97. in_format = av_find_input_format("dshow");
    98. if (in_format == nullptr) {
    99. std::cout<< "av_find_input_format error!" << std::endl;
    100. //goto _END;
    101. }
    102. //设置采样参数
    103. av_dict_set(&options, "video_size", "1280*720", 0);
    104. av_dict_set(&options, "framerate", "10", 0);
    105. av_dict_set(&options, "pixel_format", "yuyv422", 0);
    106. if ((ret = avformat_open_input(&fmt_ctx, device_name, in_format, &options)) != 0) {
    107. std::cout << "Failed to open video device," << ret << std::endl;
    108. //goto _END;
    109. }
    110. AVPacket* pkt = av_packet_alloc();
    111. av_init_packet(pkt);
    112. //sdl初始化 ------------ start -------------
    113. if (SDL_Init(SDL_INIT_VIDEO)) {
    114. qDebug() << "Could not initialize SDL";
    115. return;
    116. }
    117. SDL_Window* screen;
    118. //SDL 2.0 Support for multiple windows
    119. screen = SDL_CreateWindowFrom((void *)label->winId());
    120. //screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
    121. // screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
    122. if (!screen) {
    123. printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
    124. return;
    125. }
    126. SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    127. Uint32 pixformat = 0;
    128. //IYUV: Y + U + V (3 planes)
    129. //YV12: Y + V + U (3 planes)
    130. pixformat = SDL_PIXELFORMAT_IYUV;
    131. const int pixel_w = 1280, pixel_h = 720;
    132. SDL_Texture* sdlTexture1 = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
    133. //sdl初始化 ------------- end --------------
    134. //图像格式转换------------------------------start----------------------
    135. //图像转换参数
    136. AVFrame* frame_yuyv422 = NULL;
    137. AVFrame* frame_yuv420 = NULL;
    138. struct SwsContext* img_convert_ctx = NULL;
    139. AVPixelFormat in_pix_fmt = AV_PIX_FMT_YUYV422;
    140. AVPixelFormat out_pix_fmt = AV_PIX_FMT_YUV420P;
    141. static int sws_flags = SWS_BICUBIC; //差值算法,双三次
    142. //创建avframe
    143. frame_yuyv422 = create_frame(1280, 720, in_pix_fmt);
    144. frame_yuv420 = create_frame(1280, 720, out_pix_fmt);
    145. //设置转换context
    146. if (img_convert_ctx == NULL)
    147. {
    148. img_convert_ctx = sws_getContext(1280, 720,
    149. (AVPixelFormat)in_pix_fmt,
    150. 1280,
    151. 720,
    152. (AVPixelFormat)out_pix_fmt,
    153. sws_flags, NULL, NULL, NULL);
    154. if (img_convert_ctx == NULL)
    155. {
    156. std::cout << "Cannot initialize the conversion context\n" << std::endl;
    157. }
    158. }
    159. //图像格式转换------------------------------end----------------------
    160. SDL_Event event;
    161. SDL_Thread *timer_thread = NULL;
    162. timer_thread = SDL_CreateThread(refresh_video_timer,
    163. NULL,
    164. NULL);
    165. while (isplay) {
    166. SDL_WaitEvent(&event);
    167. if (event.type == REFRESH_EVENT){
    168. while (!av_read_frame(fmt_ctx, pkt)) {
    169. printf("Size of collected data %d\n", pkt->size);
    170. // (宽*高)*(yuv420=1.5/yuv422=2/yuv444=3)
    171. //fwrite(pkt->data, 1, 1280 * 720 *2, out_file);
    172. int numBytes = avpicture_get_size(in_pix_fmt,1280,720);
    173. uint8_t *out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    174. ret = avpicture_fill((AVPicture*)frame_yuv420, out_buffer, (AVPixelFormat)out_pix_fmt, 1280, 720);
    175. ret = avpicture_fill((AVPicture*)frame_yuyv422, (unsigned char*)pkt->data, (AVPixelFormat)in_pix_fmt, 1280, 720);
    176. std::cout << "avpicture_fill: " << ret << std::endl;
    177. // av_image_fill_arrays(frame_yuyv422->data, frame_yuyv422->linesize, pkt->data, in_pix_fmt, 1280, 720, 4);
    178. //yuyv422 -> yuv420p
    179. ret = sws_scale(img_convert_ctx, frame_yuyv422->data, frame_yuyv422->linesize,
    180. 0, 720, frame_yuv420->data, frame_yuv420->linesize);
    181. std::cout << "sws_scale-ret:" << ret<< std::endl;
    182. //SDL_UpdateTexture(sdlTexture1, NULL, pkt->data, pixel_w);
    183. SDL_UpdateTexture(sdlTexture1, NULL, frame_yuv420->data[0], 1280);
    184. SDL_RenderClear(sdlRenderer);
    185. sdlRect.x = 0;
    186. sdlRect.y = 0;
    187. sdlRect.w = pixel_w;
    188. sdlRect.h = pixel_h;
    189. SDL_RenderCopy(sdlRenderer, sdlTexture1, NULL, &sdlRect);
    190. SDL_RenderPresent(sdlRenderer);
    191. //Delay 40ms
    192. SDL_Delay(40);
    193. std::cout << "delay 40ms" << std::endl;
    194. if (out_buffer)
    195. av_free(out_buffer);
    196. }
    197. } else if (event.type == SDL_WINDOWEVENT) {
    198. //if resize
    199. //SDL_GetWindowSize(window, &w_width, &w_height);
    200. }
    201. else if (event.type == SDL_QUIT) {
    202. thread_exit = 1;
    203. } else if (event.type == QUIT_EVENT) {
    204. break;
    205. }
    206. SDL_DestroyTexture(sdlTexture1);
    207. SDL_DestroyRenderer(sdlRenderer);
    208. SDL_DestroyWindow(screen);
    209. SDL_Quit();
    210. av_packet_unref(pkt);
    211. }
    212. //_END:
    213. if (pkt) {
    214. av_packet_free(&pkt);
    215. pkt = nullptr;
    216. }
    217. if (fmt_ctx) {
    218. avformat_close_input(&fmt_ctx);
    219. fmt_ctx = nullptr;
    220. }
    221. if (frame_yuyv422)
    222. {
    223. av_frame_free(&frame_yuyv422);
    224. frame_yuyv422 = NULL;
    225. }
    226. if (frame_yuv420)
    227. {
    228. av_frame_free(&frame_yuv420);
    229. frame_yuv420 = NULL;
    230. }
    231. SDL_DestroyRenderer(sdlRenderer);
    232. SDL_DestroyWindow(screen);
    233. SDL_Quit();
    234. return;
    235. }