帮我基于ffmpeg写一个音视频播放器,要做好音视频同步
时间: 2023-05-24 08:02:06 浏览: 156
非常感谢您的提问。我可以提供以下参考代码,基于ffmpeg实现音视频同步播放器:
// C++头文件
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <chrono>
// FFmpeg头文件
extern "C" {
# include "libavutil/opt.h"
# include "libavcodec/avcodec.h"
# include "libavformat/avformat.h"
# include "libavformat/avio.h"
# include "libavutil/mathematics.h"
# include "libswscale/swscale.h"
# include "libavutil/imgutils.h"
# include "SDL2/SDL.h"
}
using namespace std;
int main(int argc, char* argv[])
{
// 初始化FFmpeg库
av_register_all();
// 创建AVFormatContext结构体
AVFormatContext* pFormatCtx = NULL;
// 打开输入视频文件
if (avformat_open_input(&pFormatCtx, fileName, NULL, NULL) < 0)
{
cerr << "Could not open input file: " << fileName << endl;
return -1;
}
// 确认输入文件中的流信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
cerr << "Could not find stream information!" << endl;
return -1;
}
// 打印文件信息
av_dump_format(pFormatCtx, 0, fileName, 0);
// 查找最佳的音频流和视频流
int audioStreamIndex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
int videoStreamIndex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
// 分配AVCodecContext结构体
AVCodecContext* pCodecCtx = NULL;
// 利用音频流或视频流的编解码器信息来获得AVCodecContext结构体
if (audioStreamIndex >= 0)
{
pCodecCtx = pFormatCtx->streams[audioStreamIndex]->codec;
}
else if (videoStreamIndex >= 0)
{
pCodecCtx = pFormatCtx->streams[videoStreamIndex]->codec;
}
else
{
cerr << "Could not find audio or video stream!" << endl;
return -1;
}
// 根据编解码器ID查找AVCodec结构体
AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (!pCodec)
{
cerr << "Could not find codec!" << endl;
return -1;
}
// 打开codec
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
cerr << "Could not open codec!" << endl;
return -1;
}
// 分配AVFrame结构体内存
AVFrame* pFrame = av_frame_alloc();
// 分配AVPacket结构体内存
AVPacket* pPacket = av_packet_alloc();
// 创建一个用于渲染视频的SDL窗口
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_Window* pWindow = SDL_CreateWindow("Media Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
pCodecCtx->width, pCodecCtx->height, SDL_WINDOW_OPENGL);
SDL_Renderer* pRenderer = SDL_CreateRenderer(pWindow, -1, 0);
SDL_Texture* pTexture = SDL_CreateTexture(pRenderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,
pCodecCtx->width, pCodecCtx->height);
// 初始化音频
SDL_AudioSpec audioDesired, audioSpec;
audioDesired.freq = pCodecCtx->sample_rate;
audioDesired.format = AUDIO_S16SYS;
audioDesired.channels = pCodecCtx->channels;
audioDesired.silence = 0;
audioDesired.samples = 512;
audioDesired.callback = audioCallback;
audioDesired.userdata = pCodecCtx;
// 打开音频设备
SDL_OpenAudio(&audioDesired, &audioSpec);
// 开始读取、解码、播放音视频数据
while (av_read_frame(pFormatCtx, pPacket) == 0)
{
if (pPacket->stream_index == audioStreamIndex)
{
// 解码并播放音频数据
decodeAudio(pPacket, pCodecCtx);
SDL_PauseAudio(0);
}
else if (pPacket->stream_index == videoStreamIndex)
{
// 解码并播放视频数据
decodeVideo(pPacket, pCodecCtx, pFrame);
SDL_UpdateYUVTexture(pTexture, NULL, pFrame->data[0],
pFrame->linesize[0], pFrame->data[1], pFrame->linesize[1],
pFrame->data[2], pFrame->linesize[2]);
SDL_RenderClear(pRenderer);
SDL_RenderCopy(pRenderer, pTexture, NULL, NULL);
SDL_RenderPresent(pRenderer);
}
av_packet_unref(pPacket);
SDL_Delay(1);
}
// 释放内存空间
SDL_DestroyTexture(pTexture);
SDL_DestroyRenderer(pRenderer);
SDL_DestroyWindow(pWindow);
SDL_CloseAudio();
av_frame_free(&pFrame);
av_packet_free(&pPacket);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
阅读全文