Created
July 5, 2023 03:02
-
-
Save ShadowPower/2a2908b28735d6174446c73ac872d70d to your computer and use it in GitHub Desktop.
ffmpeg playback with c++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "player.h" | |
using namespace std; | |
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { | |
AVAudioFifo* fifo = reinterpret_cast<AVAudioFifo*>(pDevice->pUserData); | |
av_audio_fifo_read(fifo, &pOutput, frameCount); | |
(void) pInput; | |
} | |
int main(int argc, char* argv[]) | |
{ | |
if (argc < 2) { | |
cerr << "没有文件" << endl; | |
return -1; | |
} | |
// 打开文件 | |
cout << "打开文件" << endl; | |
AVFormatContext* fmt_ctx{ nullptr }; | |
int ret = avformat_open_input(&fmt_ctx, argv[1], nullptr, nullptr); | |
if (ret < 0) { | |
cerr << "打不开文件" << endl; | |
return -1; | |
} | |
cout << "找音视频流" << endl; | |
ret = avformat_find_stream_info(fmt_ctx, nullptr); | |
if (ret < 0) { | |
cerr << "文件里没有音视频流" << endl; | |
return -1; | |
} | |
cout << "文件里面有 " << fmt_ctx->nb_streams << " 条音视频流" << endl; | |
cout << "找音轨" << endl; | |
int index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); | |
if (index < 0) { | |
cerr << "没有音轨" << endl; | |
return -1; | |
} | |
AVStream* stream = fmt_ctx->streams[index]; | |
cout << "找对应的解码器" << endl; | |
const AVCodec* decoder = avcodec_find_decoder(stream->codecpar->codec_id); | |
if (!decoder) { | |
cerr << "没有解码器" << endl; | |
return -1; | |
} | |
cout << "创建解码器" << endl; | |
AVCodecContext* codec_ctx{ avcodec_alloc_context3(decoder) }; | |
avcodec_parameters_to_context(codec_ctx, stream->codecpar); | |
ret = avcodec_open2(codec_ctx, decoder, nullptr); | |
if (ret < 0) { | |
cerr << "解码器创建失败" << endl; | |
return -1; | |
} | |
// 解码 | |
AVPacket* packet = av_packet_alloc(); | |
AVFrame* frame = av_frame_alloc(); | |
AVCodecParameters* codec_par = stream->codecpar; | |
SwrContext* resampler{swr_alloc_set_opts(nullptr, codec_par->channel_layout, AV_SAMPLE_FMT_FLT, | |
codec_par->sample_rate, codec_par->channel_layout, (AVSampleFormat) codec_par->format, | |
codec_par->sample_rate, 0, nullptr)}; | |
AVAudioFifo* fifo = av_audio_fifo_alloc(AV_SAMPLE_FMT_FLT, codec_par->channels, 1); | |
while (av_read_frame(fmt_ctx, packet) == 0) { | |
if (packet->stream_index != stream->index) { | |
continue; | |
} | |
cout << "将音频帧放进解码队列" << endl; | |
ret = avcodec_send_packet(codec_ctx, packet); | |
if (ret < 0) { | |
if (ret != AVERROR(EAGAIN)) { | |
cerr << "炸了" << endl; | |
} | |
} | |
while ((ret = avcodec_receive_frame(codec_ctx, frame)) == 0) { | |
cout << "从解码队列中取出解码结果" << endl; | |
if (ret < 0) { | |
if (ret == AVERROR(AVERROR_EOF)) { | |
// 放完了 | |
break; | |
} | |
// 其他问题 | |
break; | |
} | |
cout << "重采样PCM转成所需要的格式" << endl; | |
AVFrame* resampled_frame = av_frame_alloc(); | |
resampled_frame->sample_rate = frame->sample_rate; | |
resampled_frame->channel_layout = frame->channel_layout; | |
resampled_frame->channels = frame->channels; | |
resampled_frame->format = AV_SAMPLE_FMT_FLT; | |
ret = swr_convert_frame(resampler, resampled_frame, frame); | |
cout << "写进FIFO队列" << endl; | |
av_frame_unref(frame); | |
av_audio_fifo_write(fifo, (void**)resampled_frame->data, resampled_frame->nb_samples); | |
av_frame_free(&resampled_frame); | |
} | |
} | |
// 播放 | |
ma_device_config deviceConfig; | |
ma_device device; | |
ma_backend backends[] = { | |
ma_backend_wasapi | |
}; | |
deviceConfig = ma_device_config_init(ma_device_type_playback); | |
deviceConfig.playback.format = ma_format_f32; | |
deviceConfig.playback.channels = codec_par->channels; | |
deviceConfig.sampleRate = codec_par->sample_rate; | |
deviceConfig.dataCallback = data_callback; | |
deviceConfig.pUserData = fifo; | |
cout << "释放FFMPEG内存" << endl; | |
avformat_close_input(&fmt_ctx); | |
av_frame_free(&frame); | |
av_packet_free(&packet); | |
avcodec_free_context(&codec_ctx); | |
swr_free(&resampler); | |
cout << "初始化音频设备" << endl; | |
if (ma_device_init_ex(backends, sizeof(backends) / sizeof(backends[0]), NULL, &deviceConfig, &device) != MA_SUCCESS) { | |
cerr << "无法初始化音频设备" << endl; | |
return -1; | |
} | |
cout << "打开音频设备" << endl; | |
if (ma_device_start(&device) != MA_SUCCESS) { | |
cerr << "音频设备打开失败" << endl; | |
ma_device_uninit(&device); | |
return -1; | |
} | |
while (av_audio_fifo_size(fifo)); | |
cout << "释放FIFO队列内存" << endl; | |
av_audio_fifo_free(fifo); | |
cout << "关闭音频设备" << endl; | |
ma_device_uninit(&device); | |
getchar(); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <iostream> | |
extern "C" { | |
#include <libavcodec/avcodec.h> | |
#include <libavformat/avformat.h> | |
#include <libavutil/avutil.h> | |
#include <libavutil/audio_fifo.h> | |
#include <libswresample/swresample.h> | |
} | |
#define MINIAUDIO_IMPLEMENTATION | |
#include "miniaudio.h" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment