| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137 |
- #include "muxer_ffmpeg.h"
- #include "headers_ffmpeg.h"
- #include "muxer_define.h"
- #include "encoder_video.h"
- #include "encoder_video_factory.h"
- #include "record_desktop.h"
- #include "sws_helper.h"
- #include "encoder_aac.h"
- #include "filter_amix.h"
- #include "filter_aresample.h"
- #include "record_audio.h"
- #include "ring_buffer.h"
- #include "error_define.h"
- #include "log_helper.h"
- namespace am {
- muxer_ffmpeg::muxer_ffmpeg()
- {
- ffmpeg_register_all();
- ffmpeg_register_devices(); // 添加设备注册,支持RTSP等网络协议
- _v_stream = NULL;
- _a_stream = NULL;
- _fmt = NULL;
- _fmt_ctx = NULL;
- _base_time = -1;
- }
- muxer_ffmpeg::~muxer_ffmpeg()
- {
- stop();
- cleanup();
- }
- int muxer_ffmpeg::init(const char *output_file,
- record_desktop *source_desktop,
- record_audio **source_audios,
- const int source_audios_nb,
- const MUX_SETTING_T &setting)
- {
- int error = AE_NO;
- int ret = 0;
- do {
- al_info("start to initialize muxer ,output:%s ", output_file);
- error = alloc_oc(output_file, setting);
- if (error != AE_NO)
- break;
- if (_fmt->video_codec != AV_CODEC_ID_NONE) {
- error = add_video_stream(setting, source_desktop);
- if (error != AE_NO)
- break;
- }
- if (_fmt->audio_codec != AV_CODEC_ID_NONE && source_audios_nb) {
- error = add_audio_stream(setting, source_audios, source_audios_nb);
- if (error != AE_NO)
- break;
- }
- error = open_output(output_file, setting);
- if (error != AE_NO)
- break;
- av_dump_format(_fmt_ctx, 0, NULL, 1);
- _inited = true;
- } while (0);
- if (error != AE_NO) {
- cleanup();
- al_debug("muxer ffmpeg initialize failed:%s %d", err2str(error), ret);
- }
- return error;
- }
- int muxer_ffmpeg::start()
- {
- std::lock_guard<std::mutex> lock(_mutex);
- int error = AE_NO;
- if (_running == true) {
- return AE_NO;
- }
- if (_inited == false) {
- return AE_NEED_INIT;
- }
- _base_time = av_gettime_relative();
- if (_v_stream && _v_stream->v_enc)
- _v_stream->v_enc->start();
- if (_a_stream && _a_stream->a_enc)
- _a_stream->a_enc->start();
- if (_a_stream && _a_stream->a_nb >= 2 && _a_stream->a_filter_amix)
- _a_stream->a_filter_amix->start();
- if (_a_stream && _a_stream->a_nb < 2 && _a_stream->a_filter_aresample) {
- for (int i = 0; i < _a_stream->a_nb; i++) {
- _a_stream->a_filter_aresample[i]->start();
- }
- }
- if (_a_stream && _a_stream->a_src) {
- for (int i = 0; i < _a_stream->a_nb; i++) {
- if (_a_stream->a_src[i])
- _a_stream->a_src[i]->start();
- }
- }
- if (_v_stream && _v_stream->v_src)
- _v_stream->v_src->start();
- _running = true;
- return error;
- }
- int muxer_ffmpeg::stop()
- {
- std::lock_guard<std::mutex> lock(_mutex);
- if (_running == false)
- return AE_NO;
- _running = false;
- al_debug("try to stop muxer....");
- al_debug("stop audio recorder...");
- if (_a_stream && _a_stream->a_src) {
- for (int i = 0; i < _a_stream->a_nb; i++) {
- _a_stream->a_src[i]->stop();
- }
- }
- al_debug("stop video recorder...");
- if (_v_stream && _v_stream->v_src)
- _v_stream->v_src->stop();
- al_debug("stop audio amix filter...");
- if (_a_stream && _a_stream->a_filter_amix)
- _a_stream->a_filter_amix->stop();
- al_debug("stop audio aresampler filter...");
- if (_a_stream && _a_stream->a_filter_aresample) {
- for (int i = 0; i < _a_stream->a_nb; i++) {
- _a_stream->a_filter_aresample[i]->stop();
- }
- }
- al_debug("stop video encoder...");
- if (_v_stream && _v_stream->v_enc)
- _v_stream->v_enc->stop();
- al_debug("stop audio encoder...");
- if (_a_stream) {
- if (_a_stream->a_enc)
- _a_stream->a_enc->stop();
- }
- al_debug("write file trailer...");
- if (_fmt_ctx)
- av_write_trailer(_fmt_ctx); //must write trailer ,otherwise file can not play
- al_debug("muxer stopped...");
- return AE_NO;
- }
- int muxer_ffmpeg::pause()
- {
- _paused = true;
- return 0;
- }
- int muxer_ffmpeg::resume()
- {
- _paused = false;
- return 0;
- }
- void muxer_ffmpeg::on_desktop_data(AVFrame *frame)
- {
- if (_running == false || _paused == true || !_v_stream || !_v_stream->v_enc
- || !_v_stream->v_sws) {
- return;
- }
- int len = 0, ret = AE_NO;
- uint8_t *yuv_data = NULL;
- ret = _v_stream->v_sws->convert(frame, &yuv_data, &len);
- if (ret == AE_NO && yuv_data && len) {
- _v_stream->v_enc->put(yuv_data, len, frame);
- if (_on_yuv_data && _preview_enabled == true)
- _on_yuv_data(yuv_data, len, frame->width, frame->height, 0);
- }
- }
- void muxer_ffmpeg::on_desktop_error(int error)
- {
- al_fatal("on desktop capture error:%d", error);
- }
- int getPcmDB(const unsigned char *pcmdata, size_t size)
- {
- int db = 0;
- float value = 0;
- double sum = 0;
- double average = 0;
- int bit_per_sample = 32;
- int byte_per_sample = bit_per_sample / 8;
- int channel_num = 2;
- for (int i = 0; i < size; i += channel_num * byte_per_sample) {
- memcpy(&value, pcmdata + i, byte_per_sample);
- sum += abs(value);
- }
- average = sum / (double) (size / byte_per_sample / channel_num);
- if (average > 0) {
- db = (int) (20 * log10f(average));
- }
- al_debug("%d %f %f", db, average, sum);
- return db;
- }
- static int pcm_fltp_db_count(AVFrame *frame, int channels)
- {
- int i = 0, ch = 0;
- int ndb = 0;
- float value = 0.;
- float *ch_left = (float *) frame->data[0];
- //float *ch_right = (float *)frame->data[1];
- for (i = 0; i < frame->nb_samples; i++) {
- value += fabs(ch_left[i]);
- }
- value = value / frame->nb_samples;
- if (0 != value) {
- ndb = (int) (20.0 * log10((value / 1.0)));
- } else
- ndb = -100;
- return ndb;
- }
- void muxer_ffmpeg::on_audio_data(AVFrame *frame, int index)
- {
- if (_running == false || _paused == true)
- return;
- if (_a_stream->a_filter_amix != nullptr)
- _a_stream->a_filter_amix->add_frame(frame, index);
- else if (_a_stream->a_filter_aresample != nullptr
- && _a_stream->a_filter_aresample[index] != nullptr) {
- _a_stream->a_filter_aresample[index]->add_frame(frame);
- }
- return;
- }
- void muxer_ffmpeg::on_audio_error(int error, int index)
- {
- al_fatal("on audio capture error:%d with stream index:%d", error, index);
- }
- void muxer_ffmpeg::on_filter_amix_data(AVFrame *frame, int)
- {
- if (_running == false || !_a_stream->a_enc)
- return;
- AUDIO_SAMPLE *resamples = _a_stream->a_resamples[0];
- int copied_len = 0;
- int sample_len = ffmpeg_get_buffer_size((AVSampleFormat) frame->format,
- ffmpeg_get_frame_channels(frame),
- frame->nb_samples,
- 1);
- sample_len = ffmpeg_get_buffer_size((AVSampleFormat) frame->format,
- ffmpeg_get_frame_channels(frame),
- frame->nb_samples,
- 1);
- #ifdef _DEBUG
- //al_debug("dg:%d", pcm_fltp_db_count(frame, frame->channels));
- #endif
- int remain_len = sample_len;
- //for data is planar,should copy data[0] data[1] to correct buff pos
- if (av_sample_fmt_is_planar((AVSampleFormat) frame->format) == 0) {
- while (remain_len > 0) {
- //cache pcm
- copied_len = min(resamples->size - resamples->sample_in, remain_len);
- if (copied_len) {
- memcpy(resamples->buff + resamples->sample_in,
- frame->data[0] + sample_len - remain_len,
- copied_len);
- resamples->sample_in += copied_len;
- remain_len = remain_len - copied_len;
- }
- //got enough pcm to encoder,resample and mix
- if (resamples->sample_in == resamples->size) {
- _a_stream->a_enc->put(resamples->buff, resamples->size, frame);
- resamples->sample_in = 0;
- }
- }
- } else { //resample size is channels*frame->linesize[0],for 2 channels
- while (remain_len > 0) {
- copied_len = min(resamples->size - resamples->sample_in, remain_len);
- if (copied_len) {
- memcpy(resamples->buff + resamples->sample_in / 2,
- frame->data[0] + (sample_len - remain_len) / 2,
- copied_len / 2);
- memcpy(resamples->buff + resamples->size / 2 + resamples->sample_in / 2,
- frame->data[1] + (sample_len - remain_len) / 2,
- copied_len / 2);
- resamples->sample_in += copied_len;
- remain_len = remain_len - copied_len;
- }
- if (resamples->sample_in == resamples->size) {
- _a_stream->a_enc->put(resamples->buff, resamples->size, frame);
- resamples->sample_in = 0;
- }
- }
- }
- }
- void muxer_ffmpeg::on_filter_amix_error(int error, int)
- {
- al_fatal("on filter amix audio error:%d", error);
- }
- void muxer_ffmpeg::on_filter_aresample_data(AVFrame *frame, int index)
- {
- if (_running == false || !_a_stream->a_enc)
- return;
- AUDIO_SAMPLE *resamples = _a_stream->a_resamples[0];
- int copied_len = 0;
- int sample_len = ffmpeg_get_buffer_size((AVSampleFormat) frame->format,
- ffmpeg_get_frame_channels(frame),
- frame->nb_samples,
- 1);
- sample_len = ffmpeg_get_buffer_size((AVSampleFormat) frame->format,
- ffmpeg_get_frame_channels(frame),
- frame->nb_samples,
- 1);
- int remain_len = sample_len;
- //for data is planar,should copy data[0] data[1] to correct buff pos
- if (av_sample_fmt_is_planar((AVSampleFormat) frame->format) == 0) {
- while (remain_len > 0) {
- //cache pcm
- copied_len = min(resamples->size - resamples->sample_in, remain_len);
- if (copied_len) {
- memcpy(resamples->buff + resamples->sample_in,
- frame->data[0] + sample_len - remain_len,
- copied_len);
- resamples->sample_in += copied_len;
- remain_len = remain_len - copied_len;
- }
- //got enough pcm to encoder,resample and mix
- if (resamples->sample_in == resamples->size) {
- _a_stream->a_enc->put(resamples->buff, resamples->size, frame);
- resamples->sample_in = 0;
- }
- }
- } else { //resample size is channels*frame->linesize[0],for 2 channels
- while (remain_len > 0) {
- copied_len = min(resamples->size - resamples->sample_in, remain_len);
- if (copied_len) {
- memcpy(resamples->buff + resamples->sample_in / 2,
- frame->data[0] + (sample_len - remain_len) / 2,
- copied_len / 2);
- memcpy(resamples->buff + resamples->size / 2 + resamples->sample_in / 2,
- frame->data[1] + (sample_len - remain_len) / 2,
- copied_len / 2);
- resamples->sample_in += copied_len;
- remain_len = remain_len - copied_len;
- }
- if (resamples->sample_in == resamples->size) {
- _a_stream->a_enc->put(resamples->buff, resamples->size, frame);
- resamples->sample_in = 0;
- }
- }
- }
- }
- void muxer_ffmpeg::on_filter_aresample_error(int error, int index)
- {
- al_fatal("on filter aresample[%d] audio error:%d", index, error);
- }
- void muxer_ffmpeg::on_enc_264_data(AVPacket *packet)
- {
- if (_running && _v_stream) {
- write_video(packet);
- }
- }
- void muxer_ffmpeg::on_enc_264_error(int error)
- {
- al_fatal("on desktop encode error:%d", error);
- }
- void muxer_ffmpeg::on_enc_aac_data(AVPacket *packet)
- {
- if (_running && _a_stream) {
- write_audio(packet);
- }
- }
- void muxer_ffmpeg::on_enc_aac_error(int error)
- {
- al_fatal("on audio encode error:%d", error);
- }
- int muxer_ffmpeg::alloc_oc(const char *output_file, const MUX_SETTING_T &setting)
- {
- _output_file = std::string(output_file);
- int error = AE_NO;
- int ret = 0;
- do {
- // 检查协议类型并指定相应的输出格式
- const char* format_name = NULL;
- std::string url_str(output_file);
- if (url_str.find("rtmp://") == 0 || url_str.find("rtmps://") == 0) {
- format_name = "flv";
- } else if (url_str.find("rtsp://") == 0) {
- // RTSP推流使用RTSP muxer,由FFmpeg管理RTP会话
- format_name = "rtsp";
- al_debug("RTSP URL detected, using RTSP format");
- }
-
- ret = avformat_alloc_output_context2(&_fmt_ctx, NULL, format_name, output_file);
- if (ret < 0 || !_fmt_ctx) {
- al_debug("avformat_alloc_output_context2 failed with ret=%d, format=%s, url=%s",
- ret, format_name ? format_name : "auto", output_file);
- error = AE_FFMPEG_ALLOC_CONTEXT_FAILED;
- break;
- }
- _fmt = _fmt_ctx->oformat;
- } while (0);
- return error;
- }
- int muxer_ffmpeg::add_video_stream(const MUX_SETTING_T &setting, record_desktop *source_desktop)
- {
- int error = AE_NO;
- int ret = 0;
- _v_stream = new MUX_STREAM();
- memset(_v_stream, 0, sizeof(MUX_STREAM));
- _v_stream->v_src = source_desktop;
- _v_stream->pre_pts = -1;
- _v_stream->v_src->registe_cb(std::bind(&muxer_ffmpeg::on_desktop_data,
- this,
- std::placeholders::_1),
- std::bind(&muxer_ffmpeg::on_desktop_error,
- this,
- std::placeholders::_1));
- RECORD_DESKTOP_RECT v_rect = _v_stream->v_src->get_rect();
- do {
- error = encoder_video_new(setting.v_encoder_id, &_v_stream->v_enc);
- if (error != AE_NO)
- break;
- error = _v_stream->v_enc->init(setting.v_out_width,
- setting.v_out_height,
- setting.v_frame_rate,
- setting.v_bit_rate,
- setting.v_qb);
- if (error != AE_NO)
- break;
- _v_stream->v_enc->registe_cb(std::bind(&muxer_ffmpeg::on_enc_264_data,
- this,
- std::placeholders::_1),
- std::bind(&muxer_ffmpeg::on_enc_264_error,
- this,
- std::placeholders::_1));
- _v_stream->v_sws = new sws_helper();
- error = _v_stream->v_sws->init(_v_stream->v_src->get_pixel_fmt(),
- v_rect.right - v_rect.left,
- v_rect.bottom - v_rect.top,
- AV_PIX_FMT_YUV420P,
- setting.v_out_width,
- setting.v_out_height);
- if (error != AE_NO)
- break;
- const AVCodec *codec = avcodec_find_encoder(_v_stream->v_enc->get_codec_id());
- if (!codec) {
- error = AE_FFMPEG_FIND_ENCODER_FAILED;
- break;
- }
- // FFmpeg 7兼容性:不再直接修改AVOutputFormat的video_codec字段
- // 编码器信息通过AVStream的codecpar设置
- AVStream *st = avformat_new_stream(_fmt_ctx, codec);
- if (!st) {
- error = AE_FFMPEG_NEW_STREAM_FAILED;
- break;
- }
- ffmpeg_set_stream_codec_id(st, _v_stream->v_enc->get_codec_id());
- ffmpeg_set_stream_bit_rate(st, setting.v_bit_rate);
- ffmpeg_set_stream_codec_type(st, AVMEDIA_TYPE_VIDEO);
- // 使用编码器/设置的帧率作为时间基(1/fps),避免异常的帧率显示
- st->time_base = {1, setting.v_frame_rate};
- ffmpeg_set_stream_pix_fmt(st, AV_PIX_FMT_YUV420P);
- ffmpeg_set_stream_dimensions(st, setting.v_out_width, setting.v_out_height);
- // 正确设置平均帧率为 fps/1
- st->avg_frame_rate = {setting.v_frame_rate, 1};
- // 始终为视频流设置extradata(SPS/PPS等),RTSP/SDP需要该信息
- {
- uint8_t *extradata = (uint8_t *) av_memdup(_v_stream->v_enc->get_extradata(),
- _v_stream->v_enc->get_extradata_size());
- ffmpeg_set_stream_extradata(st, extradata, _v_stream->v_enc->get_extradata_size());
- }
- _v_stream->st = st;
- _v_stream->setting = setting;
- //_v_stream->filter = av_bitstream_filter_init("h264_mp4toannexb");
- } while (0);
- return error;
- }
- int muxer_ffmpeg::add_audio_stream(const MUX_SETTING_T &setting,
- record_audio **source_audios,
- const int source_audios_nb)
- {
- int error = AE_NO;
- int ret = 0;
- _a_stream = new MUX_STREAM();
- memset(_a_stream, 0, sizeof(MUX_STREAM));
- _a_stream->a_nb = source_audios_nb;
- _a_stream->a_filter_aresample = new filter_aresample *[_a_stream->a_nb];
- _a_stream->a_resamples = new AUDIO_SAMPLE *[_a_stream->a_nb];
- _a_stream->a_samples = new AUDIO_SAMPLE *[_a_stream->a_nb];
- _a_stream->a_src = new record_audio *[_a_stream->a_nb];
- _a_stream->pre_pts = -1;
- do {
- _a_stream->a_enc = new encoder_aac();
- error = _a_stream->a_enc->init(setting.a_nb_channel,
- setting.a_sample_rate,
- setting.a_sample_fmt,
- setting.a_bit_rate);
- if (error != AE_NO)
- break;
- _a_stream->a_enc->registe_cb(std::bind(&muxer_ffmpeg::on_enc_aac_data,
- this,
- std::placeholders::_1),
- std::bind(&muxer_ffmpeg::on_enc_aac_error,
- this,
- std::placeholders::_1));
- for (int i = 0; i < _a_stream->a_nb; i++) {
- _a_stream->a_src[i] = source_audios[i];
- _a_stream->a_src[i]->registe_cb(std::bind(&muxer_ffmpeg::on_audio_data,
- this,
- std::placeholders::_1,
- std::placeholders::_2),
- std::bind(&muxer_ffmpeg::on_audio_error,
- this,
- std::placeholders::_1,
- std::placeholders::_2),
- i);
- _a_stream->a_filter_aresample[i] = new filter_aresample();
- _a_stream->a_resamples[i] = new AUDIO_SAMPLE({NULL, 0, 0});
- FILTER_CTX ctx_in = {0}, ctx_out = {0};
- ctx_in.time_base = _a_stream->a_src[i]->get_time_base();
- ctx_in.channel_layout = ffmpeg_get_default_channel_layout(
- _a_stream->a_src[i]->get_channel_num());
- ctx_in.nb_channel = _a_stream->a_src[i]->get_channel_num();
- ctx_in.sample_fmt = _a_stream->a_src[i]->get_fmt();
- ctx_in.sample_rate = _a_stream->a_src[i]->get_sample_rate();
- ctx_out.time_base = {1, AV_TIME_BASE};
- ctx_out.channel_layout = ffmpeg_get_default_channel_layout(setting.a_nb_channel);
- ctx_out.nb_channel = setting.a_nb_channel;
- ctx_out.sample_fmt = setting.a_sample_fmt;
- ctx_out.sample_rate = setting.a_sample_rate;
- _a_stream->a_filter_aresample[i]->init(ctx_in, ctx_out, i);
- _a_stream->a_filter_aresample[i]->registe_cb(
- std::bind(&muxer_ffmpeg::on_filter_aresample_data,
- this,
- std::placeholders::_1,
- std::placeholders::_2),
- std::bind(&muxer_ffmpeg::on_filter_aresample_error,
- this,
- std::placeholders::_1,
- std::placeholders::_2));
- _a_stream->a_resamples[i]->size
- = av_samples_get_buffer_size(NULL,
- setting.a_nb_channel,
- _a_stream->a_enc->get_nb_samples(),
- setting.a_sample_fmt,
- 1);
- _a_stream->a_resamples[i]->buff = new uint8_t[_a_stream->a_resamples[i]->size];
- _a_stream->a_samples[i] = new AUDIO_SAMPLE({NULL, 0, 0});
- _a_stream->a_samples[i]->size
- = av_samples_get_buffer_size(NULL,
- _a_stream->a_src[i]->get_channel_num(),
- _a_stream->a_enc->get_nb_samples(),
- _a_stream->a_src[i]->get_fmt(),
- 1);
- _a_stream->a_samples[i]->buff = new uint8_t[_a_stream->a_samples[i]->size];
- }
- if (_a_stream->a_nb >= 2) {
- _a_stream->a_filter_amix = new am::filter_amix();
- error = _a_stream->a_filter_amix->init({NULL,
- NULL,
- _a_stream->a_src[0]->get_time_base(),
- _a_stream->a_src[0]->get_sample_rate(),
- _a_stream->a_src[0]->get_fmt(),
- _a_stream->a_src[0]->get_channel_num(),
- (int64_t)ffmpeg_get_default_channel_layout(
- _a_stream->a_src[0]->get_channel_num())},
- {NULL,
- NULL,
- _a_stream->a_src[1]->get_time_base(),
- _a_stream->a_src[1]->get_sample_rate(),
- _a_stream->a_src[1]->get_fmt(),
- _a_stream->a_src[1]->get_channel_num(),
- (int64_t)ffmpeg_get_default_channel_layout(
- _a_stream->a_src[1]->get_channel_num())},
- {NULL,
- NULL,
- {1, AV_TIME_BASE},
- setting.a_sample_rate,
- setting.a_sample_fmt,
- setting.a_nb_channel,
- (int64_t)ffmpeg_get_default_channel_layout(
- setting.a_nb_channel)});
- if (error != AE_NO) {
- break;
- }
- _a_stream->a_filter_amix->registe_cb(std::bind(&muxer_ffmpeg::on_filter_amix_data,
- this,
- std::placeholders::_1,
- std::placeholders::_2),
- std::bind(&muxer_ffmpeg::on_filter_amix_error,
- this,
- std::placeholders::_1,
- std::placeholders::_2));
- }
- const AVCodec *codec = avcodec_find_encoder(_a_stream->a_enc->get_codec_id());
- if (!codec) {
- error = AE_FFMPEG_FIND_ENCODER_FAILED;
- break;
- }
- // FFmpeg 7兼容性:不再直接修改AVOutputFormat的codec字段
- // 这些字段在新版本中是只读的,编码器信息通过AVStream设置
- AVCodecID audio_codec_id = _a_stream->a_enc->get_codec_id();
- AVStream *st = avformat_new_stream(_fmt_ctx, codec);
- if (!st) {
- error = AE_FFMPEG_NEW_STREAM_FAILED;
- break;
- }
- av_dict_set(&st->metadata, "title", "Track1", 0);
- // 设置音频流编码器参数
- ffmpeg_set_stream_codec_id(st, audio_codec_id);
- ffmpeg_set_stream_codec_type(st, AVMEDIA_TYPE_AUDIO);
- ffmpeg_set_stream_bit_rate(st, setting.a_bit_rate);
-
- // 设置采样率到AVStream的codecpar中(FFmpeg 7兼容性)
- #if FFMPEG_VERSION_MAJOR >= 7
- st->codecpar->sample_rate = setting.a_sample_rate;
- av_channel_layout_default(&st->codecpar->ch_layout, setting.a_nb_channel);
- st->codecpar->format = setting.a_sample_fmt;
- #elif FFMPEG_VERSION_MAJOR >= 4
- st->codecpar->sample_rate = setting.a_sample_rate;
- st->codecpar->channels = setting.a_nb_channel;
- st->codecpar->channel_layout = ffmpeg_get_default_channel_layout(setting.a_nb_channel);
- st->codecpar->format = setting.a_sample_fmt;
- #else
- st->codec->sample_rate = setting.a_sample_rate;
- st->codec->channels = setting.a_nb_channel;
- st->codec->channel_layout = ffmpeg_get_default_channel_layout(setting.a_nb_channel);
- st->codec->sample_fmt = setting.a_sample_fmt;
- #endif
-
- st->time_base = {1, setting.a_sample_rate};
- AVCodecContext *codec_ctx = ffmpeg_get_codec_context(st);
- codec_ctx->bit_rate = setting.a_bit_rate;
- ffmpeg_set_codec_channels(codec_ctx, setting.a_nb_channel);
- codec_ctx->sample_rate = setting.a_sample_rate;
- codec_ctx->sample_fmt = setting.a_sample_fmt;
- codec_ctx->time_base = {1, setting.a_sample_rate};
- ffmpeg_set_codec_channel_layout(codec_ctx, ffmpeg_get_default_channel_layout(setting.a_nb_channel));
- // 检查是否为RTMP推流(FLV格式)
- bool isRtmpStream = (_fmt_ctx->oformat && strcmp(_fmt_ctx->oformat->name, "flv") == 0);
-
- if (_fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
- if (isRtmpStream) {
- // RTMP推流不使用GLOBAL_HEADER,保持ADTS格式
- al_debug("RTMP stream detected, not setting GLOBAL_HEADER for AAC");
- } else {
- // 其他格式使用GLOBAL_HEADER
- codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
-
- codec_ctx->extradata_size
- = _a_stream->a_enc->get_extradata_size();
- codec_ctx->extradata = (uint8_t *) av_memdup(_a_stream->a_enc->get_extradata(),
- _a_stream->a_enc->get_extradata_size());
- al_debug("Non-RTMP stream, setting GLOBAL_HEADER for AAC");
- }
- }
- // 始终为音频流设置extradata(AudioSpecificConfig),RTSP/SDP/MP4/FLV等容器需要该信息
- if (_a_stream->a_enc->get_extradata_size() > 0) {
- uint8_t *asc = (uint8_t *)av_memdup(_a_stream->a_enc->get_extradata(),
- _a_stream->a_enc->get_extradata_size());
- ffmpeg_set_stream_extradata(st, asc, _a_stream->a_enc->get_extradata_size());
- al_debug("Set AAC extradata on stream: size=%d", _a_stream->a_enc->get_extradata_size());
- } else {
- al_warn("AAC extradata size is 0; some outputs (e.g., RTSP/MP4) may fail in avformat_write_header");
- }
- _a_stream->st = st;
- _a_stream->setting = setting;
- // 对于RTMP推流,不使用aac_adtstoasc过滤器,保持ADTS格式
- // 检查输出格式是否为FLV(RTMP使用FLV容器)
- if (_fmt_ctx->oformat && strcmp(_fmt_ctx->oformat->name, "flv") == 0) {
- // RTMP推流,不使用过滤器
- _a_stream->filter = nullptr;
- al_debug("RTMP output detected, skipping aac_adtstoasc filter");
- } else {
- // 其他格式(如MP4/RTSP),使用aac_adtstoasc过滤器以去除可能存在的ADTS头
- _a_stream->filter = ffmpeg_bitstream_filter_init("aac_adtstoasc");
- al_debug("Non-RTMP output, using aac_adtstoasc filter");
- }
- if (_fmt_ctx->oformat) {
- const char *ofmt = _fmt_ctx->oformat->name;
- // FLV/RTMP、RTSP、MP4等容器需要原始AAC(无ADTS),因此启用 aac_adtstoasc 过滤器
- if (strcmp(ofmt, "flv") == 0 || strcmp(ofmt, "rtsp") == 0 || strcmp(ofmt, "mp4") == 0
- || strcmp(ofmt, "mov") == 0 || strcmp(ofmt, "matroska") == 0) {
- _a_stream->filter = ffmpeg_bitstream_filter_init("aac_adtstoasc");
- al_debug("Container '%s' requires raw AAC, enabling aac_adtstoasc filter", ofmt);
- } else {
- // 对于期望ADTS的容器(如 mpegts/adts),不使用过滤器
- _a_stream->filter = nullptr;
- al_debug("Container '%s' supports ADTS, skipping aac_adtstoasc filter", ofmt);
- }
- }
- } while (0);
- return error;
- }
- int muxer_ffmpeg::open_output(const char *output_file, const MUX_SETTING_T &setting)
- {
- int error = AE_NO;
- int ret = 0;
- do {
- if (!(_fmt->flags & AVFMT_NOFILE)) {
- ret = avio_open(&_fmt_ctx->pb, output_file, AVIO_FLAG_WRITE);
- if (ret < 0) {
- error = AE_FFMPEG_OPEN_IO_FAILED;
- break;
- }
- }
- AVDictionary *opt = NULL;
-
- // 检查是否为RTSP推流,添加特定参数
- std::string url_str(output_file);
- if (url_str.find("rtsp://") == 0) {
- // RTSP推流特定参数设置
- av_dict_set(&opt, "rtsp_transport", "tcp", 0); // 使用TCP传输,更稳定
- av_dict_set(&opt, "muxdelay", "0.1", 0); // 设置复用延迟
- av_dict_set(&opt, "fflags", "+genpts", 0); // 生成PTS
- al_debug("RTSP output detected, setting specific parameters");
- } else {
- // 非RTSP推流的原有参数
- av_dict_set_int(&opt, "video_track_timescale", _v_stream->setting.v_frame_rate, 0);
- }
- ret = avformat_write_header(_fmt_ctx, &opt);
- av_dict_free(&opt);
- if (ret < 0) {
- al_debug("avformat_write_header failed with ret=%d, error=%s", ret, av_err2str(ret));
- error = AE_FFMPEG_WRITE_HEADER_FAILED;
- break;
- }
- } while (0);
- return error;
- }
- void muxer_ffmpeg::cleanup_video()
- {
- if (!_v_stream)
- return;
- if (_v_stream->v_enc)
- delete _v_stream->v_enc;
- if (_v_stream->v_sws)
- delete _v_stream->v_sws;
- delete _v_stream;
- _v_stream = nullptr;
- }
- void muxer_ffmpeg::cleanup_audio()
- {
- if (!_a_stream)
- return;
- if (_a_stream->a_enc)
- delete _a_stream->a_enc;
- if (_a_stream->a_filter_amix)
- delete _a_stream->a_filter_amix;
-
- // 释放bitstream过滤器
- if (_a_stream->filter) {
- ffmpeg_bitstream_filter_close(_a_stream->filter);
- _a_stream->filter = nullptr;
- }
- if (_a_stream->a_nb) {
- for (int i = 0; i < _a_stream->a_nb; i++) {
- if (_a_stream->a_filter_aresample && _a_stream->a_filter_aresample[i])
- delete _a_stream->a_filter_aresample[i];
- if (_a_stream->a_samples && _a_stream->a_samples[i]) {
- delete[] _a_stream->a_samples[i]->buff;
- delete _a_stream->a_samples[i];
- }
- if (_a_stream->a_resamples && _a_stream->a_resamples[i]) {
- delete[] _a_stream->a_resamples[i]->buff;
- delete _a_stream->a_resamples[i];
- }
- }
- if (_a_stream->a_filter_aresample)
- delete[] _a_stream->a_filter_aresample;
- if (_a_stream->a_samples)
- delete[] _a_stream->a_samples;
- if (_a_stream->a_resamples)
- delete[] _a_stream->a_resamples;
- }
- delete _a_stream;
- _a_stream = nullptr;
- }
- void muxer_ffmpeg::cleanup()
- {
- cleanup_video();
- cleanup_audio();
- if (_fmt && !(_fmt->flags & AVFMT_NOFILE))
- avio_closep(&_fmt_ctx->pb);
- if (_fmt_ctx) {
- avformat_free_context(_fmt_ctx);
- }
- _fmt_ctx = NULL;
- _fmt = NULL;
- _inited = false;
- }
- uint64_t muxer_ffmpeg::get_current_time()
- {
- std::lock_guard<std::mutex> lock(_time_mutex);
- return av_gettime_relative();
- }
- int muxer_ffmpeg::write_video(AVPacket *packet)
- {
- //must lock here,coz av_interleaved_write_frame will push packet into a queue,and is not thread safe
- std::lock_guard<std::mutex> lock(_mutex);
- packet->stream_index = _v_stream->st->index;
- /*packet->pts = av_rescale_q_rnd(packet->pts,
- _v_stream->v_src->get_time_base(),
- { 1,AV_TIME_BASE },
- (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
- // make audio and video use one clock
- if (_v_stream->pre_pts == (uint64_t)-1) {
- _v_stream->pre_pts = packet->pts;
- }*/
- // scale ts with timebase of base_time
- av_packet_rescale_ts(packet, _v_stream->v_src->get_time_base(), {1, AV_TIME_BASE});
- // make audio and video use one clock
- packet->pts = packet->pts - _base_time;
- packet->dts = packet->pts; //make sure that dts is equal to pts
- av_packet_rescale_ts(packet, {1, AV_TIME_BASE}, _v_stream->st->time_base);
- al_debug("V:%lld", packet->pts);
- av_assert0(packet->data != NULL);
- int ret = av_interleaved_write_frame(_fmt_ctx,
- packet); //no need to unref packet,this will be auto unref
- if (ret != 0) {
- al_fatal("write video frame error:%d", ret);
- }
- return ret;
- }
- int muxer_ffmpeg::write_audio(AVPacket *packet)
- {
- std::lock_guard<std::mutex> lock(_mutex);
- packet->stream_index = _a_stream->st->index;
- AVRational src_timebase = {1, 1};
- if (_a_stream->a_filter_amix != nullptr) {
- src_timebase = _a_stream->a_filter_amix->get_time_base();
- } else {
- src_timebase = _a_stream->a_filter_aresample[0]->get_time_base();
- }
- /*packet->pts = av_rescale_q_rnd(packet->pts,
- src_timebase,
- { 1,AV_TIME_BASE },
- (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
-
- if (_v_stream->pre_pts == (uint64_t)-1) {
- _v_stream->pre_pts = packet->pts;
- }*/
- // 检查时间戳有效性
- if (packet->pts == AV_NOPTS_VALUE) {
- al_warn("Audio packet has invalid timestamp (AV_NOPTS_VALUE), using current time");
- packet->pts = av_gettime_relative() - _base_time;
- packet->dts = packet->pts;
- } else {
- // scale ts with timebase of base_time
- av_packet_rescale_ts(packet, src_timebase, {1, AV_TIME_BASE});
-
- // make audio and video use one clock - 只有当时间戳有效时才减去base_time
- if (packet->pts >= _base_time) {
- packet->pts = packet->pts - _base_time;
- } else {
- // 如果时间戳小于base_time,使用相对时间
- packet->pts = av_gettime_relative() - _base_time;
- }
- packet->dts = packet->pts; //make sure that dts is equal to pts
- }
- av_packet_rescale_ts(packet, {1, AV_TIME_BASE}, _a_stream->st->time_base);
- al_debug("A:%lld %lld", packet->pts, packet->dts);
- // 验证音频包的基本有效性
- if (packet->data == NULL) {
- al_error("Audio packet data is null, skipping write");
- return -1;
- }
-
- // 验证AAC包大小,确保符合RTMP推流要求
- if (packet->size < 7) {
- al_warn("AAC packet size too small: %d bytes (minimum 7 required for ADTS), skipping write", packet->size);
- return -1;
- }
-
- // 检查是否为RTMP推流(FLV格式)
- bool isRtmpStream = (_fmt_ctx->oformat && strcmp(_fmt_ctx->oformat->name, "flv") == 0);
-
- if (isRtmpStream) {
- // RTMP推流需要验证ADTS头部
- if (packet->size >= 2) {
- // 检查ADTS同步字(0xFFF)
- if ((packet->data[0] != 0xFF) || ((packet->data[1] & 0xF0) != 0xF0)) {
- al_warn("Invalid AAC ADTS header for RTMP: 0x%02X%02X, packet may be corrupted",
- packet->data[0], packet->data[1]);
- // 对于RTMP,这可能导致播放问题,但仍然尝试发送
- }
- }
- al_debug("RTMP AAC packet: size=%d, header=0x%02X%02X",
- packet->size, packet->data[0], packet->data[1]);
- }
- // 根据容器判断是否期望ADTS头部
- bool expectsAdts = false;
- if (_fmt_ctx->oformat && _fmt_ctx->oformat->name) {
- const char *ofmt = _fmt_ctx->oformat->name;
- if (strcmp(ofmt, "mpegts") == 0 || strcmp(ofmt, "adts") == 0) {
- expectsAdts = true;
- }
- }
- if (expectsAdts) {
- // 期望ADTS:做最基本校验
- if (packet->size < 7) {
- al_warn("AAC packet too small for ADTS: %d bytes (min 7)", packet->size);
- return -1;
- }
- if (packet->size >= 2 && (packet->data[0] != 0xFF || (packet->data[1] & 0xF0) != 0xF0)) {
- al_warn("Missing ADTS syncword in ADTS-expected container, header=0x%02X%02X",
- packet->data[0],
- packet->data[1]);
- }
- } else {
- // 不期望ADTS(如 RTSP/FLV/MP4 等):如果存在ADTS,仅打印调试信息
- if (packet->size >= 2 && packet->data[0] == 0xFF && (packet->data[1] & 0xF0) == 0xF0) {
- al_debug(
- "ADTS header present but container doesn't require it; filter may strip it later");
- }
- }
- // 应用bitstream过滤器(如果存在)
- AVPacket *filtered_packet = packet;
- if (_a_stream->filter) {
- AVPacket *temp_packet = av_packet_alloc();
- if (temp_packet) {
- av_packet_ref(temp_packet, packet);
- int filter_ret = ffmpeg_bitstream_filter_filter(_a_stream->filter, temp_packet);
- if (filter_ret >= 0) {
- // 验证过滤后的包大小
- if (temp_packet->size < 2) {
- al_warn("Filtered AAC packet too small: %d bytes, using original packet",
- temp_packet->size);
- av_packet_free(&temp_packet);
- } else {
- filtered_packet = temp_packet;
- al_debug("Applied aac_adtstoasc filter, original size: %d, filtered size: %d",
- packet->size,
- temp_packet->size);
- }
- } else {
- al_warn("Failed to apply bitstream filter: %d, using original packet", filter_ret);
- av_packet_free(&temp_packet);
- }
- } else {
- al_warn("Failed to allocate temporary packet for filtering");
- }
- }
- int ret = av_interleaved_write_frame(_fmt_ctx, filtered_packet);
-
- // 如果使用了过滤器,需要释放临时包
- if (filtered_packet != packet) {
- av_packet_free(&filtered_packet);
- }
- if (ret != 0) {
- al_fatal("write audio frame error:%d", ret);
- }
- return ret;
- }
- } // namespace am
|