ffmpeg + SDL2 はプレーヤーを実装します (1)

使用している ffmpeg のバージョンは 5.0.1 で、SDL のバージョンは 2.022 です。C++環境はvs2017です。

最も単純な全体コードが最初に実装され、SDL と ffmpeg の組み合わせが最初に実現されます。

#include<iostream>
#include<string.h>
#include<SDL.h>
extern "C"
{
#include "libavformat/avformat.h" //头文件不仅要在项目中鼠标点击配置,在代码中也要引入
#include "include/libavformat/avformat.h"
#include "include/libswscale/swscale.h"
#include "include/libavdevice/avdevice.h"
#include"libavcodec/avcodec.h"
}
using namespace std;

int main(int argc, char *argv[])
{
	int ret = 1;
	const char *file = "ds.mov";

	// 初始化ffmpeg组件
	AVFormatContext *pFormatCtx = nullptr;   //视频文件上下文
	int videostream;   //视频流标识
	AVCodecParameters *pCodeParameters = nullptr; //解码器相关参数
	const AVCodec *pCodec = nullptr;  //解码器
	AVCodecContext *pCodecCtx = nullptr;  // 解码器上下文
	AVFrame *pFrame = nullptr; //解码后的帧
	AVPacket packet; // 解码前的帧

	//初始化SDL组件
	SDL_Rect rect;                              //渲染显示面积
	SDL_Window *window = NULL;                  // 窗口
	SDL_Renderer *renderer = NULL;              // 渲染
	SDL_Texture *texture = NULL;                // 纹理
	Uint32 pixformat;

	//视频分辨率
	int w_width = 640;  
	int w_height = 480;

	//SDL初始化 
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		cout << "can not initialize SDL" << endl;
		return ret;
	}

	//FFMPEG 视频文件读取
	if (avformat_open_input(&pFormatCtx, file, nullptr, nullptr) != 0) {
		cout << "can not open the video file" << endl;
		goto __FAIL;
	}

	//FFMPEG 寻找视频流
	videostream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1,nullptr,0);
	if (videostream == -1) {
		cout << "can not open a video stream" << endl;
		goto __FAIL;
	}

	//FFMPEG 寻找合适的解码器
	pCodeParameters = pFormatCtx->streams[videostream]->codecpar;
	pCodec = avcodec_find_decoder(pCodeParameters->codec_id);
	if (pCodec == nullptr) {
		cout << "can not find a codec" << endl;
		goto __FAIL;
	}

	//FFMPEG 解码器信息配置
	pCodecCtx = avcodec_alloc_context3(pCodec);
	if (avcodec_parameters_to_context(pCodecCtx, pCodeParameters) != 0) {
		cout << "can not copy codec context" << endl;
		goto __FAIL;
	}

	//FFMPEG 解码器启动
	if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
		cout << " can not open the decoder" << endl;
		goto __FAIL;
	}

	//FFMPEG 初始化解码的帧
	pFrame = av_frame_alloc();

	//SDL 获得显示的视频画面的长度与宽度
	w_width = pCodecCtx->width;
	w_height = pCodecCtx->height;

	//SDL 窗口初始化
	window = SDL_CreateWindow("MEDIA PLAYER",
		SDL_WINDOWPOS_UNDEFINED,
		SDL_WINDOWPOS_UNDEFINED,
		w_width, w_height,
		SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
	if (!window) {
		cout << "can not create window" << endl;
		goto __FAIL;
	}

	//SDL 渲染器初始化
	renderer = SDL_CreateRenderer(window, -1, 0);
	if (!renderer) {
		cout << "can not create renderer!" << endl;
		goto __FAIL;
	}

	//SDL 视频格式与纹理初始化
	pixformat = SDL_PIXELFORMAT_IYUV;
	texture = SDL_CreateTexture(renderer,
		pixformat,
		SDL_TEXTUREACCESS_STREAMING,
		w_width,
		w_height);

	//主循环
	while (av_read_frame(pFormatCtx,&packet)>= 0) {//FFMPEG 如果已经读到了一个帧
		if (packet.stream_index == videostream) {//并且该帧是视频帧
			avcodec_send_packet(pCodecCtx,&packet);
			while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
				//SDL 刷新纹理
				SDL_UpdateYUVTexture(texture, NULL,
					pFrame->data[0], pFrame->linesize[0],
					pFrame->data[1], pFrame->linesize[1],
					pFrame->data[2], pFrame->linesize[2]);
				rect.x = 0;//SDL设置渲染目标的显示区域
				rect.y = 0;
				rect.w = pCodecCtx->width;
				rect.h = pCodecCtx->height;

				SDL_RenderClear(renderer);//SDL 清空渲染器内容
				SDL_RenderCopy(renderer, texture, NULL, &rect);//SDL 将纹理复制到渲染器
				SDL_RenderPresent(renderer);//SDL 渲染
			}
		}
		av_packet_unref(&packet);

		SDL_Event event;// SDL事件
		SDL_PollEvent(&event);// 轮询事件
		switch (event.type)
		{
		case SDL_QUIT: //如果窗口被关闭
			goto __QUIT;
		default:
			break;
		}
	}
	

__QUIT:
	ret = 0;
__FAIL:
	if (pFrame) {
		av_frame_free(&pFrame);
		pFrame = nullptr;
	}
	if (pCodecCtx) {
		avcodec_close(pCodecCtx);
		pCodecCtx = nullptr;
		pCodec = nullptr;
	}
	if (pCodeParameters) {
		avcodec_parameters_free(&pCodeParameters);
		pCodeParameters = nullptr;
	}
	/*if (pFormatCtx) {
		avformat_close_input(&pFormatCtx);
	}*/

	if (pFormatCtx) {
		avformat_free_context(pFormatCtx);
		pFormatCtx = nullptr;
	}
	
	if (texture) {
		SDL_DestroyTexture(texture);
		texture = nullptr;
	}
	if (renderer) {
		SDL_DestroyRenderer(renderer);
		renderer = nullptr;
	}
	if (window) {
		SDL_DestroyWindow(window);
		window = nullptr;
	}

	SDL_Quit();
	cout << "succeed!" << endl;
	return ret;
}

上記のコードは、主要な機能のコメントでどのモジュールに属しているかを示しています。これは、解体と学習に役立ちます。

上記のコードの内容は前回の記事で紹介したものがほとんどで、ここでは新たに出てきた知識を中心に解説します。

av_read_frame(pFormatCtx,&packet)

ビデオ フレームをデコードするときは、まずビデオ ファイルからフレームを抽出する必要があります。現在のフレームは圧縮されておらず、パケットに属していることに注意してください。復習すると、パケットはデコードされていないフレームで、フレームはデコードされたフレームです。

Av_read_frame() はまさにこの関数を実装しています. この関数はビデオファイルからパケットを正確に読み取ります. そして、この機能により、読み取りフレームが完全であることを保証できます。

前の記事で説明したように、パケットには 1 つのビデオ フレームのみが含まれますが、複数のオーディオ フレームを含めることができます。

对于音频,如果每个帧具有已知的固定大小(例如PCM或ADPCM数据),则它包含整数帧数。
如果每个帧的大小可变(MPEG),则包含一个帧。

この関数をメイン ループの動作条件として使用することで、ビデオ ファイルからマルチメディア フレームが読み込まれた後に動作し、マルチメディア フレームが読み込めない場合は動作しないことが保証されます。

avcodec_send_packet(pCodecCtx,&パケット)

この関数はまさにffmpegが映像をデコードする関数であり、動作内容はその名の通りで、パケットデータをデコード用のコーデックに転送し、デコードされたフレームデータを取得します。

avcodec_receive_frame(pCodecCtx, pFrame)

この関数はデコード動作と連携する関数です. コーデックデコーダがパケットをフレームにデコードした後, この関数を使用してフレームを読み込みます. フレーム変数は現在のデコードされたフレームです.

ここで、2 つの関数 avcodec_receive_frame(pCodecCtx, pFrame) と avcodec_send_packet(pCodecCtx,&packet) は 1 対 1 で対応していることに注意してください。実際、上記のように、パケットは複数のフレーム (オーディオ フレームの場合) を解決する可能性があるため、送信関数は複数の受信関数に対応する必要があるため、そうすべきではありません。

関数が何回実行されるかわからない場合は、この関数を while() ループの判定文に入れることができます. 関数の戻り値が変わる場合は、関数を実行する必要がないことを意味します.ループ内の関連処理 ステートメントは実行されなくなります。

SDLとffmpegの組み合わせ

前回の記事からわかるように、SDL はビデオ フレームをテクスチャに "ブラシをかけ" てからレンダリングと再生を実現します. ffmpeg でデコードされたフレームをテクスチャにブラシをかけ、両者の組み合わせを実現します.

SDL_UpdateYUVTexture(texture, NULL,
					pFrame->data[0], pFrame->linesize[0],
					pFrame->data[1], pFrame->linesize[1],
					pFrame->data[2], pFrame->linesize[2]);

テクスチャを更新する関数では、y、u、v の 3 つのチャネルのデータがそれぞれ読み込まれます。これは、テクスチャにブラシをかけることと同じであり、その後の操作は SDL 再生ビデオの操作と変わりません。

おすすめ

転載: blog.csdn.net/m0_37872216/article/details/124727130