通过解码之后得到的yuv
视频数据我们直接可以进行播放,本篇使用SDL
来实现视频播放。
SDL环境配置:https://blog.csdn.net/furzoom/article/details/53992124
SDL视频播放基础教程: https://blog.csdn.net/xuyankuanrong/article/details/77574152
有了上一篇文章的解码的基础,代码看着就很轻松了。
代码上写的很详细了,具体看代码
#include "stdafx.h"
#include <iostream>
// LNK2019 无法解析的外部符号 SDL_main,该符号在函数 main_utf8 中被引用
#define SDL_MAIN_HANDLED
//Refresh Event
#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)
#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libsdl/SDL.h>
}
void log_s(const char * msg, int d = -1123) {
if (d == -1123) {
printf_s("%s\n", msg);
}
else {
printf_s("%s %d \n", msg, d);
}
}
int thread_exit = 0;
int thread_pause = 0;
int sfp_refresh_thread(void *opaque) {
while (!thread_exit) {
if (!thread_pause) {
SDL_Event event;
event.type = SFM_REFRESH_EVENT;
SDL_PushEvent(&event);
}
SDL_Delay(10);
}
thread_exit = 0;
thread_pause = 0;
//Break
SDL_Event event;
event.type = SFM_BREAK_EVENT;
SDL_PushEvent(&event);
return 0;
}
int play(const char* filePath) {
AVFormatContext * pFmtCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
AVFrame *pFrame = NULL;
AVFrame *pFrameYUV = NULL;
uint8_t *outBuffer = NULL;
AVPacket *pPacket = NULL;
SwsContext *pSwsCtx = NULL;
//SDL
int screen_w, screen_h;
SDL_Window *screen=NULL;
SDL_Renderer* sdlRenderer = NULL;
SDL_Texture* sdlTexture = NULL;
SDL_Rect sdlRect;
SDL_Thread *video_tid = NULL;
SDL_Event event;
//1. 初始化
av_register_all();
avformat_network_init();
//2. AVFormatContext获取
pFmtCtx = avformat_alloc_context();
//3. 打开文件
if (avformat_open_input(&pFmtCtx, filePath, NULL, NULL) != 0) {
log_s("Couldn't open input stream.\n");
return -1;
}
//4. 获取文件信息
if (avformat_find_stream_info(pFmtCtx, NULL)<0) {
log_s("Couldn't find stream information.");
return -1;
}
//5. 获取视频的index
int i = 0, videoIndex = -1;
for (; i < pFmtCtx->nb_streams; i++) {
if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoIndex = i;
break;
}
}
if (videoIndex == -1) {
log_s("Didn't find a video stream.");
return -1;
}
//6. 获取解码器并打开
pCodecCtx = avcodec_alloc_context3(NULL);
if (avcodec_parameters_to_context(pCodecCtx, pFmtCtx->streams[videoIndex]->codecpar) < 0) {
log_s("Didn't parameters to contex.");
return -1;
}
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
log_s("Codec not found.");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL)<0) {//打开解码器
log_s("Could not open codec.");
return -1;
}
//7. 解码播放开始准备工作
pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc();
//根据需要解码的类型,获取需要的buffer,不要忘记free
outBuffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1) * sizeof(uint8_t));
//根据指定的图像参数和提供的数组设置数据指针和行数 ,数据填充到对应的pFrameYUV里面
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, outBuffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);
pPacket = av_packet_alloc();
log_s("--------------- File Information ----------------");
av_dump_format(pFmtCtx, 0, filePath, 0);
log_s("-------------------------------------------------");
//获取SwsContext
pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, NULL, NULL, NULL, NULL);
//----------------------------------------------------------------------------------------------------------------
// 8. SDL相关初始化
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
log_s("Could not initialize SDL - ");
log_s(SDL_GetError());
return -1;
}
screen_w = pCodecCtx->width;
screen_h = pCodecCtx->height;
screen = SDL_CreateWindow("WS ffmpeg player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
pCodecCtx->width/2, pCodecCtx->height/2, SDL_WINDOW_OPENGL);
if (!screen) {
log_s("SDL: could not create window - exiting");
log_s(SDL_GetError());
return -1;
}
sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = screen_w;
sdlRect.h = screen_h;
video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
//----------------------------------------------------------------------------------------------------------------
int count = 0;
//9.读取数据播放
for (;;) {
//Wait
SDL_WaitEvent(&event);
if (event.type == SFM_REFRESH_EVENT) {
if (av_read_frame(pFmtCtx, pPacket) == 0) {
if (pPacket->stream_index == videoIndex) {
if (avcodec_send_packet(pCodecCtx, pPacket) != 0) {//解码一帧压缩数据
log_s("Decode end or Error.");
break;
}
else {//处理解码数据
avcodec_receive_frame(pCodecCtx, pFrame);
if (sws_scale(pSwsCtx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize) == 0) {
continue;
}
count++;
//SDL播放---------------------------
SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
SDL_RenderClear(sdlRenderer);
//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect);
SDL_RenderCopy(sdlRenderer, sdlTexture, &sdlRect, NULL);
SDL_RenderPresent(sdlRenderer);
//SDL End-----------------------
log_s("Succeed to play frame!", count);
}
}
}else {
//退出线程
thread_exit = 1;
av_packet_unref(pPacket);
}
}
else if (event.type == SDL_KEYDOWN) {
log_s("Pause");
//Pause
if (event.key.keysym.sym == SDLK_SPACE)
thread_pause = !thread_pause;
}
else if (event.type == SDL_QUIT) {
log_s("quit");
thread_exit = 1;
break;
}
else if (event.type == SFM_BREAK_EVENT) {
log_s("break");
break;
}
}
//sdl退出
SDL_Quit();
//回收
if (pSwsCtx != NULL) {
sws_freeContext(pSwsCtx);
}
if (outBuffer != NULL) {
av_free(outBuffer);
}
if (pFrameYUV != NULL) {
av_frame_free(&pFrameYUV);
}
if (pFrame != NULL) {
av_frame_free(&pFrame);
}
if (pCodecCtx != NULL) {
avcodec_close(pCodecCtx);
}
if (pFmtCtx != NULL) {
avformat_close_input(&pFmtCtx);
}
}
int main()
{
play("F:/视频资源/gxsp.mp4");
getchar();
return 0;
}
参考链接:https://blog.csdn.net/king1425/article/details/71171142