代码拉取完成,页面将自动刷新
#include <iostream>
#include <semaphore.h>
#include <string>
struct SwsContext;
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
#include <SDL2/SDL.h>
}
#define AUDIO_BUFFER_SIZE 1024
// 音频解码上下文
// AVFormatContext* formatContext = nullptr;
// AVCodecContext* codecContext = nullptr;
// const AVCodec *codec = nullptr;
// int audioStreamIndex = -1;
// SwrContext* swrContext = nullptr;
// AVFrame* frame = nullptr;
// AVPacket* packet = nullptr;
// 定义音频帧结构体
typedef struct AudioFrame {
uint8_t *data;
int size;
double pts;
struct AudioFrame *next;
} AudioFrame;
// 定义音频帧队列结构体
typedef struct AudioFrameQueue {
AudioFrame *head;
AudioFrame *tail;
int count;
int max_count; // 最大队列长度
pthread_mutex_t mutex;
sem_t sem;
sem_t space_sem; // 可用空间信号量
} AudioFrameQueue;
struct PlayerState {
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
SDL_Texture *texture = nullptr;
// 视频相关
AVFormatContext *pFormatCtx = nullptr;
AVCodecContext *audioCodecCtx{};
int audio_stream_index = -1;
SwrContext *swrCtx{};
SDL_AudioDeviceID audioDevice{};
AudioFrameQueue audioFrameQueue{};
bool quit = false;
// 帧率控制相关
int64_t lastFramePts = AV_NOPTS_VALUE;
Uint32 lastFrameTime = 0; // 记录上一帧的显示时间
double frameDuration = 0; // 每一帧的理想显示时间间隔
};
#define MAX_AUDIO_FRAME_SIZE 8096 // 一帧音频最大长度(样本数),该值不能太小
int audio_len = 0; // 一帧PCM音频的数据长度
unsigned char *audio_pos = nullptr; // 当前读取的位置
// 音频播放回调函数
//音频设备需要更多数据的时候会调用该回调函数
void audioCallback(void* userdata, Uint8* stream, int len) {
SDL_memset(stream, 0, len); // 将缓冲区清零
if (audio_len == 0) {
return;
}
len = (len > audio_len ? audio_len : len);
// 将音频数据混合到缓冲区
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
int main(int argc, char* argv[]) {
std::string inputFile = "D:\\videoa.mp4";;
PlayerState playerState;
// 初始化 FFmpeg
// 打开输入文件
if (avformat_open_input(&playerState.pFormatCtx, inputFile.c_str(), nullptr, nullptr) != 0) {
std::cerr << "Could not open input file" << std::endl;
return 1;
}
// 查找流信息
if (avformat_find_stream_info(playerState.pFormatCtx, nullptr) < 0) {
std::cerr << "Could not find stream information" << std::endl;
avformat_close_input(&playerState.pFormatCtx);
return 1;
}
// 查找音频流
for (int i = 0; i < playerState.pFormatCtx->nb_streams; i++) {
if (playerState.pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
playerState.audio_stream_index = i;
break;
}
}
if (playerState.audio_stream_index == -1) {
std::cerr << "Could not find audio stream" << std::endl;
avformat_close_input(&playerState.pFormatCtx);
return 1;
}
// 查找解码器
const AVCodecParameters *codecpar = playerState.pFormatCtx->streams[playerState.audio_stream_index]->codecpar;
const AVCodec* codec = avcodec_find_decoder(codecpar->codec_id);
if (!codec) {
std::cerr << "Could not find codec" << std::endl;
avformat_close_input(&playerState.pFormatCtx);
return 1;
}
// 分配解码器上下文
playerState.audioCodecCtx = avcodec_alloc_context3(codec);
if (!playerState.audioCodecCtx) {
std::cerr << "Could not allocate codec context" << std::endl;
avformat_close_input(&playerState.pFormatCtx);
return 1;
}
// 填充解码器上下文 // 把音频流中的编解码参数复制给解码器的实例
if (avcodec_parameters_to_context(playerState.audioCodecCtx, codecpar) < 0) {
std::cerr << "Could not copy codec parameters to context" << std::endl;
avcodec_free_context(&playerState.audioCodecCtx);
avformat_close_input(&playerState.pFormatCtx);
return 1;
}
// 打开解码器
if (avcodec_open2(playerState.audioCodecCtx, codec, nullptr) < 0) {
std::cerr << "Could not open codec" << std::endl;
avcodec_free_context(&playerState.audioCodecCtx);
avformat_close_input(&playerState.pFormatCtx);
return 1;
}
// 初始化重采样上下文
AVChannelLayout out_ch_layout = AV_CHANNEL_LAYOUT_STEREO; // 输出的声道布局
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; // 输出的采样格式
int out_sample_rate = 44100; // 输出的采样率
int out_nb_samples = playerState.audioCodecCtx->frame_size; // 输出的采样数量
int out_channels = out_ch_layout.nb_channels; // 输出的声道数量
if (out_nb_samples == 0) {
av_log(NULL, AV_LOG_ERROR, "unknown audio nb_samples\n");
return false;
}
int ret = swr_alloc_set_opts2(&playerState.swrCtx, &out_ch_layout, out_sample_fmt, out_sample_rate,
&playerState.audioCodecCtx->ch_layout, playerState.audioCodecCtx->sample_fmt,
playerState.audioCodecCtx->sample_rate, 0, nullptr);
if (ret < 0) {
std::cerr << "swr_alloc_set_opts2 Failed to set swr context options: " << ret << std::endl;
return false;
}
if (swr_init(playerState.swrCtx) < 0) {
std::cerr << "Could not initialize resampler" << std::endl;
avcodec_free_context(&playerState.audioCodecCtx);
avformat_close_input(&playerState.pFormatCtx);
swr_free(&playerState.swrCtx);
return 1;
}
// 计算输出的缓冲区大小
int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
std::cout << "out_buffer_size=" << out_buffer_size << '\n';
// 分配输出缓冲区的空间
auto *out_buff = static_cast<unsigned char *>(av_malloc(MAX_AUDIO_FRAME_SIZE * out_channels));
std::cout << "begin SDL_Init" << '\n';
// 初始化 SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
avcodec_free_context(&playerState.audioCodecCtx);
avformat_close_input(&playerState.pFormatCtx);
swr_free(&playerState.swrCtx);
return 1;
}
// 初始化SDL音频
SDL_AudioSpec wantSpec, haveSpec; // 声明SDL音频参数
// SDL_zero(wantSpec);
wantSpec.freq = out_sample_rate; // 采样频率
wantSpec.format = AUDIO_S16SYS; // 采样格式
wantSpec.channels = out_channels; // 声道数量
wantSpec.silence = 0; // 是否静音
wantSpec.samples = out_nb_samples; // 采样数量
wantSpec.callback = audioCallback;; //fill_audio; // 回调函数的名称
wantSpec.userdata = NULL; // 回调函数的额外信息,如果没有额外信息就填NULL
if ((playerState.audioDevice = SDL_OpenAudio(&wantSpec, NULL)) < 0) { // 打开扬声器
std::cerr << "open audio occur error" << '\n';
return -1;
}
// 分配帧和数据包
AVFrame *frame = av_frame_alloc();
AVPacket *packet = av_packet_alloc();
// 开始播放音频
SDL_PauseAudio(0);
// 等待音频播放完成
while (av_read_frame(playerState.pFormatCtx, packet) >= 0) { // 轮询数据包
if (packet->stream_index == playerState.audio_stream_index) {
// 把未解压的数据包发给解码器实例
ret = avcodec_send_packet(playerState.audioCodecCtx, packet);
// 发送数据包到解码器
if (ret == 0) {
// 接收解码后的帧
while (avcodec_receive_frame(playerState.audioCodecCtx, frame) == 0) {
// 重采样
// int dst_nb_samples = av_rescale_rnd(swr_get_delay(playerState.swrCtx, playerState.audioCodecCtx->sample_rate) + frame->nb_samples,
// 44100, playerState.audioCodecCtx->sample_rate, AV_ROUND_UP);
//
// av_samples_alloc(dst_data, nullptr, 2, dst_nb_samples, AV_SAMPLE_FMT_S16, 0);
// swr_convert(playerState.swrCtx, dst_data, dst_nb_samples, const_cast<const uint8_t **>(frame->data), frame->nb_samples);
//
// while (audio_len > 0) { // 如果还没播放完,就等待1ms
// SDL_Delay(1); // 延迟若干时间,单位毫秒
// }
// audio_pos = (unsigned char *) out_buff; // 把音频数据同步到缓冲区位置
// audio_len = out_buffer_size; // 缓冲区大小
// 重采样。也就是把输入的音频数据根据指定的采样规格转换为新的音频数据输出
swr_convert(playerState.swrCtx, // 音频采样器的实例
&out_buff, MAX_AUDIO_FRAME_SIZE, // 输出的数据内容和数据大小
(const uint8_t **) frame->data, frame->nb_samples); // 输入的数据内容和数据大小
while (audio_len > 0) { // 如果还没播放完,就等待1ms
SDL_Delay(1); // 延迟若干时间,单位毫秒
}
audio_pos = (unsigned char *) out_buff; // 把音频数据同步到缓冲区位置
audio_len = out_buffer_size; // 缓冲区大小
break;
}
} else {
std::cerr << "Could not decode audio packet" << std::endl;
}
// if (is_stop) { // 如果停止播放,就跳出循环结束解码
// break;
// }
}
av_packet_unref(packet);
}
std::cerr << "Success play audio file" << '\n';
// 停止播放音频
SDL_PauseAudio(1);
// 关闭音频设备
SDL_CloseAudio();
// 清理资源
SDL_Quit();
av_frame_free(&frame);
av_packet_free(&packet);
avcodec_free_context(&playerState.audioCodecCtx);
avformat_close_input(&playerState.pFormatCtx);
swr_free(&playerState.swrCtx);
return 0;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。