1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <iostream>

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include <SDL.h>
}


#define UPDATE_ONE_FRAME (SDL_USEREVENT +1)
#define EXIT (SDL_USEREVENT +2)
#undef main

using namespace std;

SDL_Window* sdl_window;
SDL_Renderer* sdl_render;
SDL_Texture* sdl_texture;
SDL_Rect sdl_rect;
int thread_exit = 0;
bool is_video_index = 0;

int decode_one_frame(void* arg)
{
while (!thread_exit)
{
SDL_Event event;
event.type = UPDATE_ONE_FRAME;
SDL_PushEvent(&event);
if (is_video_index)
{
SDL_Delay(40);
}
else
{
SDL_Delay(1);
}
}
SDL_Event breakEvent;
breakEvent.type = EXIT;
SDL_PushEvent(&breakEvent);
return 0;
}


int initSDL(const char* title, int &video_w, int &video_h, int &padding)
{
int window_w = video_w + padding + padding;
int window_h = video_h + padding + padding;
if (SDL_Init(SDL_INIT_VIDEO)) {
cout << "init sdl failed!" << endl;
return -1;
}
sdl_window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
window_w, window_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
sdl_render = SDL_CreateRenderer(sdl_window, -1, 0);
uint32_t pix_format = SDL_PIXELFORMAT_IYUV;
sdl_texture = SDL_CreateTexture(sdl_render, pix_format, SDL_TEXTUREACCESS_STREAMING, video_w, video_h);
}


int main()
{
AVFormatContext* format_ctx = NULL;
AVCodecContext* codec_ctx;
AVCodecParameters* codec_params = NULL;
const AVCodec* codec;
AVPacket* packet = av_packet_alloc();
AVFrame* frame = av_frame_alloc();
AVFrame* yuv_frame = av_frame_alloc();
int video_stream_index = -1;
const char* file_name = "./one_piece.mp4";
//打开文件
int ret = avformat_open_input(&format_ctx, file_name, NULL, NULL);
if (ret < 0)
{
cout << "打开文件失败!" << endl;
return -1;
}
ret = avformat_find_stream_info(format_ctx, NULL);
if (ret < 0)
{
cout << "寻找流信息失败!" << endl;
return -1;
}
for (int i = 0; i < format_ctx->nb_streams; i++)
{
if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
codec_params = format_ctx->streams[i]->codecpar;
video_stream_index = i;
}
}
if (video_stream_index < 0)
{
cout << "没有视频流!" << endl;
return -1;
}
codec = avcodec_find_decoder(codec_params->codec_id);
if (codec == NULL)
{
cout << "寻找解码器失败!" << endl;
return -1;
}
codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_ctx, codec_params);
ret = avcodec_open2(codec_ctx, codec, NULL);
if (ret < 0)
{
cout << "打开解码器失败!" << endl;
return -1;
}
//av_image_get_buffer_size
//av_image_fill_arrays
int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, codec_ctx->width, codec_ctx->height, 1);
uint8_t* buffer = (uint8_t*)av_malloc(sizeof(uint8_t) * buffer_size);
av_image_fill_arrays(yuv_frame->data, yuv_frame->linesize, buffer, AV_PIX_FMT_YUV420P,
codec_ctx->width, codec_ctx->height, 1);
SwsContext* img_convert_ctx = sws_getContext(
codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P,
SWS_BICUBIC,NULL, NULL, NULL);
int padding = 10;
initSDL(file_name, codec_ctx->width, codec_ctx->height, padding);
SDL_Thread* video_play_thread = SDL_CreateThread(decode_one_frame,NULL,NULL);
SDL_Event event;
while (true)
{
SDL_WaitEvent(&event);
if (event.type == UPDATE_ONE_FRAME)
{
cout << "接收到SDL 消息 UPDATE_ONE_FRAME" << endl;
if (av_read_frame(format_ctx, packet) >= 0)
{
if (packet->stream_index == video_stream_index)
{
is_video_index = 1;
avcodec_send_packet(codec_ctx, packet);
while (avcodec_receive_frame(codec_ctx, frame) >= 0)
{
sws_scale(img_convert_ctx, frame->data, frame->linesize, 0, codec_ctx->height,
yuv_frame->data, yuv_frame->linesize);
SDL_UpdateTexture(sdl_texture, NULL, yuv_frame->data[0], yuv_frame->linesize[0]);
SDL_RenderClear(sdl_render);
sdl_rect.x = padding;
sdl_rect.y = padding;
sdl_rect.w = codec_ctx->width;
sdl_rect.h = codec_ctx->height;
SDL_RenderCopy(sdl_render, sdl_texture, NULL, &sdl_rect);
SDL_RenderPresent(sdl_render);
}
av_packet_unref(packet);
}
else
{
is_video_index = 0;
}
}
}
else if (event.type == EXIT)
{
cout << "接收到SDL 消息 EXIT" << endl;
break;
}
else if (event.type == SDL_QUIT)
{
cout << "接收到SDL 消息 SDL_QUIT" << endl;
thread_exit = 1;
}
}
sws_freeContext(img_convert_ctx);

SDL_Quit();
av_frame_free(&yuv_frame);
av_frame_free(&frame);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return 0;
}

代码是在雷霄骅博士视频的基础上替换了一些新的API。

视频地址:基于FFmpeg+SDL的视频播放器的制作-第4节-3_哔哩哔哩_bilibili

博客地址:雷霄骅的博客_CSDN博客-FFMPEG,FFmpeg,视频质量评价领域博主