Ver Fonte

能正常推流

zhuizhu há 7 meses atrás
pai
commit
baabc2b16e

+ 297 - 10
libs/Recorder/encoder_aac.cpp

@@ -22,6 +22,8 @@ encoder_aac::encoder_aac()
     _buff = NULL;
     _buff_size = 0;
 
+    _bsf_ctx = NULL;
+
     _cond_notify = false;
 
 #ifdef SAVE_AAC
@@ -67,17 +69,85 @@ int encoder_aac::init(int nb_channels, int sample_rate, AVSampleFormat fmt, int
         _encoder_ctx->sample_fmt = fmt;
         _encoder_ctx->bit_rate = bit_rate;
 
+        // 设置AAC profile为LC,确保RTMP兼容性
+        _encoder_ctx->profile = FF_PROFILE_AAC_LOW;
         _encoder_ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
         _encoder_ctx->time_base.den = sample_rate;
         _encoder_ctx->time_base.num = 1;
-        _encoder_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
-
-        ret = avcodec_open2(_encoder_ctx, _encoder, NULL);
+        
+        // 设置编码器选项,确保生成高质量AAC数据
+        AVDictionary *options = NULL;
+        av_dict_set(&options, "aac_coder", "twoloop", 0);
+        av_dict_set(&options, "aac_pns", "1", 0);
+        av_dict_set(&options, "aac_is", "1", 0);
+        av_dict_set(&options, "aac_ms", "1", 0);
+        av_dict_set(&options, "aac_ltp", "0", 0);
+        av_dict_set(&options, "aac_pred", "0", 0);
+        
+        // 强制启用ADTS头部格式,确保RTMP兼容性
+        // 注意:"f"选项对编码器无效,需要在muxer层面处理ADTS格式
+        // av_dict_set(&options, "f", "adts", 0);
+        
+        // 对于RTMP推流,确保生成ADTS头部
+        // 不设置GLOBAL_HEADER,让每个包都包含ADTS头
+        _encoder_ctx->flags &= ~AV_CODEC_FLAG_GLOBAL_HEADER;
+        
+        ret = avcodec_open2(_encoder_ctx, _encoder, &options);
+        av_dict_free(&options);
         if (ret < 0) {
             err = AE_FFMPEG_OPEN_CODEC_FAILED;
             break;
         }
 
+        // 调试信息:输出编码器配置
+        al_debug("AAC encoder initialized: frame_size=%d, extradata_size=%d, profile=%d", 
+                 _encoder_ctx->frame_size, _encoder_ctx->extradata_size, _encoder_ctx->profile);
+        
+        // 初始化BSF将raw AAC转换为ADTS格式
+        // 注意:我们需要手动添加ADTS头部,因为没有直接的BSF可以将raw AAC转换为ADTS
+        // 暂时跳过BSF初始化,使用手动ADTS头部添加
+        _bsf_ctx = NULL;
+        al_debug("Skipping BSF initialization, will add ADTS headers manually");
+        
+        /*
+        const AVBitStreamFilter *bsf = av_bsf_get_by_name("aac_adtstoasc");
+        if (!bsf) {
+            al_error("Failed to find aac_adtstoasc BSF");
+            err = AE_FFMPEG_FIND_BSF_FAILED;
+            break;
+        }
+        
+        ret = av_bsf_alloc(bsf, &_bsf_ctx);
+        if (ret < 0) {
+            al_error("Failed to allocate BSF context: %d", ret);
+            err = AE_FFMPEG_ALLOC_BSF_FAILED;
+            break;
+        }
+        
+        // 复制编码器参数到BSF
+        ret = avcodec_parameters_from_context(_bsf_ctx->par_in, _encoder_ctx);
+        if (ret < 0) {
+            al_error("Failed to copy parameters to BSF: %d", ret);
+            err = AE_FFMPEG_BSF_PARAMS_FAILED;
+            break;
+        }
+        
+        ret = av_bsf_init(_bsf_ctx);
+        if (ret < 0) {
+            al_error("Failed to initialize BSF: %d", ret);
+            err = AE_FFMPEG_BSF_INIT_FAILED;
+            break;
+        }
+        
+        al_debug("BSF initialized successfully for ADTS conversion");
+        */
+        
+        // 确保frame_size有效
+        if (_encoder_ctx->frame_size <= 0) {
+            al_warn("AAC encoder frame_size is invalid: %d, setting to 1024", _encoder_ctx->frame_size);
+            _encoder_ctx->frame_size = 1024;
+        }
+
         _buff_size = av_samples_get_buffer_size(NULL, nb_channels, _encoder_ctx->frame_size, fmt, 1);
         _buff = (uint8_t *) av_malloc(_buff_size);
 
@@ -218,25 +288,92 @@ AVCodecID encoder_aac::get_codec_id()
 
 int encoder_aac::encode(AVFrame *frame, AVPacket *packet)
 {
+    // 添加输入帧验证
+    if (frame) {
+        al_debug("Input frame: samples=%d, channels=%d, format=%d, pts=%lld", 
+                 frame->nb_samples, ffmpeg_get_frame_channels(frame), frame->format, frame->pts);
+    }
+    
     int ret = avcodec_send_frame(_encoder_ctx, frame);
+    al_debug("avcodec_send_frame returned: %d", ret);
     if (ret < 0) {
+        al_error("avcodec_send_frame failed: %d", ret);
         return AE_FFMPEG_ENCODE_FRAME_FAILED;
     }
 
+    bool packetReceived = false;
     while (ret == 0) {
         av_init_packet(packet);
 
         ret = avcodec_receive_packet(_encoder_ctx, packet);
-        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
-            break;
+        al_debug("avcodec_receive_packet returned: %d", ret);
+        if (ret == AVERROR(EAGAIN)) {
+            al_debug("Encoder needs more input: %d (this is normal)", ret);
+            break; // 需要更多输入,这是正常情况
+        }
+        
+        if (ret == AVERROR_EOF) {
+            al_debug("Encoder EOF: %d", ret);
+            break; // 编码器已结束
         }
 
         if (ret < 0) {
+            al_error("avcodec_receive_packet failed: %d", ret);
             return AE_FFMPEG_READ_PACKET_FAILED;
         }
 
-        if (ret == 0 && _on_data)
-            _on_data(packet);
+        if (ret == 0) {
+            packetReceived = true;
+            
+            // 如果输入帧有有效的时间戳,将其传递给输出包
+            if (frame && frame->pts != AV_NOPTS_VALUE) {
+                packet->pts = frame->pts;
+                packet->dts = frame->pts; // 对于音频,通常dts等于pts
+                al_debug("Timestamp inherited from frame: pts=%lld, dts=%lld", packet->pts, packet->dts);
+            } else {
+                al_warn("Input frame has invalid timestamp (AV_NOPTS_VALUE), packet will have invalid timestamp");
+            }
+            
+            // 立即检查编码器输出的原始包大小
+            al_debug("Raw encoder output: size=%d, data=%p", packet->size, packet->data);
+            
+            // 验证AAC数据包的有效性
+            if (packet->data == nullptr) {
+                al_warn("AAC packet data is null, skipping");
+                av_packet_unref(packet);
+                continue;
+            }
+            
+            // 验证音频包大小,确保符合RTMP要求
+            // AAC数据包至少需要包含ADTS头部(7字节)+ 音频数据
+            if (packet->size < 7) {
+                al_warn("AAC packet size too small: %d bytes (minimum 7 required), skipping", packet->size);
+                av_packet_unref(packet);
+                continue;
+            }
+            
+            // 检查ADTS头部的同步字(0xFFF)
+            if (packet->size >= 2 && (packet->data[0] != 0xFF || (packet->data[1] & 0xF0) != 0xF0)) {
+                al_debug("Raw AAC data detected, adding ADTS header");
+                // 为raw AAC数据添加ADTS头部
+                int adts_ret = add_adts_header(packet);
+                if (adts_ret != AE_NO) {
+                    al_error("Failed to add ADTS header: %d", adts_ret);
+                    av_packet_unref(packet);
+                    continue;
+                }
+            } else {
+                al_debug("ADTS header already present");
+            }
+            
+            al_debug("AAC packet: size=%d, pts=%lld, dts=%lld, header=0x%02X%02X", 
+                     packet->size, packet->pts, packet->dts, 
+                     packet->size >= 2 ? packet->data[0] : 0,
+                     packet->size >= 2 ? packet->data[1] : 0);
+            
+            if (_on_data)
+                _on_data(packet);
+        }
 
 #ifdef SAVE_AAC
         av_packet_rescale_ts(packet, _encoder_ctx->time_base, _aac_stream->time_base);
@@ -247,6 +384,11 @@ int encoder_aac::encode(AVFrame *frame, AVPacket *packet)
         av_packet_unref(packet);
     }
 
+    // 如果没有收到数据包且不是EAGAIN错误,可能有问题
+    if (!packetReceived && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
+        al_warn("No packet received and unexpected return code: %d", ret);
+    }
+
     return AE_NO;
 }
 
@@ -268,6 +410,25 @@ void encoder_aac::encode_loop()
             _cond_var.wait_for(lock, std::chrono::milliseconds(300));
 
         while ((len = _ring_buffer->get(_buff, _buff_size, pcm_frame))) {
+            // 验证获取的音频数据
+            if (len <= 0) {
+                al_warn("Invalid audio data length: %d", len);
+                continue;
+            }
+            
+            // 确保音频数据正确填充到frame中
+            if (len != _buff_size) {
+                al_warn("Audio data size mismatch: expected=%d, actual=%d", _buff_size, len);
+            }
+            
+            // 添加详细的调试信息
+            static int frameCounter = 0;
+            if (++frameCounter % 100 == 0) { // 每100帧打印一次
+                al_debug("Ring buffer data: len=%d, buff_size=%d, frame_size=%d, channels=%d, format=%d", 
+                         len, _buff_size, _encoder_ctx->frame_size, 
+                         ffmpeg_get_frame_channels(_frame), _frame->format);
+            }
+            
             _frame->pts = pcm_frame.pts;
 #if FFMPEG_VERSION_MAJOR >= 7
             // In FFmpeg 7+, pkt_pts is removed, use pts instead
@@ -276,13 +437,53 @@ void encoder_aac::encode_loop()
             _frame->pkt_pts = pcm_frame.pkt_pts;
 #endif
             _frame->pkt_dts = pcm_frame.pkt_dts;
+            
+            // 验证编码器帧的配置
+            if (_frame->nb_samples != _encoder_ctx->frame_size) {
+                al_warn("Frame samples mismatch: frame->nb_samples=%d, encoder->frame_size=%d", 
+                        _frame->nb_samples, _encoder_ctx->frame_size);
+            }
+            
+            // 重新填充音频frame数据,确保数据正确
+            int ret = avcodec_fill_audio_frame(_frame, 
+                                               ffmpeg_get_frame_channels(_frame), 
+                                               (AVSampleFormat)_frame->format, 
+                                               _buff, 
+                                               len, 
+                                               0);
+            if (ret < 0) {
+                al_warn("Failed to fill audio frame: %d", ret);
+                continue;
+            }
+            
+            // 验证填充后的帧数据
+            if (!_frame->data[0]) {
+                al_warn("Frame data is null after filling");
+                continue;
+            }
+            
+            // 计算期望的数据大小并验证
+            int expectedSize = _frame->nb_samples * ffmpeg_get_frame_channels(_frame) * 
+                              av_get_bytes_per_sample((AVSampleFormat)_frame->format);
+            if (len != expectedSize) {
+                al_warn("Data size validation failed: actual=%d, expected=%d", len, expectedSize);
+            }
 
             if ((error = encode(_frame, packet)) != AE_NO) {
+                al_error("AAC encoding failed: error=%d, frame_pts=%lld, frame_size=%d", 
+                        error, _frame->pts, _frame->nb_samples);
+                
+                // 尝试恢复:跳过当前帧,继续处理下一帧
+                if (error == AE_FFMPEG_ENCODE_FRAME_FAILED || error == AE_FFMPEG_READ_PACKET_FAILED) {
+                    al_warn("Attempting to recover from encoding error, skipping current frame");
+                    continue;
+                }
+                
+                // 严重错误,通知上层并退出
                 if (_on_error)
                     _on_error(error);
 
-                al_fatal("read aac packet failed:%d", error);
-
+                al_fatal("Critical AAC encoding error:%d, stopping encoder", error);
                 break;
             }
         }
@@ -291,9 +492,17 @@ void encoder_aac::encode_loop()
     }
 
     //flush pcm data in encoder
-    encode(NULL, packet);
+    al_debug("Flushing remaining AAC data from encoder");
+    int flush_error = encode(NULL, packet);
+    if (flush_error != AE_NO) {
+        al_warn("Error during AAC encoder flush: %d", flush_error);
+    }
 
     av_packet_free(&packet);
+    
+    // 输出编码统计信息
+    al_info("AAC encoder stopped. Encoding session completed.");
+    
 #ifdef SAVE_AAC
     av_write_trailer(_aac_fmt_ctx);
 #endif
@@ -320,6 +529,11 @@ void encoder_aac::cleanup()
 
     _buff = NULL;
 
+    if (_bsf_ctx) {
+        av_bsf_free(&_bsf_ctx);
+        _bsf_ctx = NULL;
+    }
+
     _encoder_ctx = NULL;
 
 #ifdef SAVE_AAC
@@ -329,4 +543,77 @@ void encoder_aac::cleanup()
     }
 #endif
 }
+
+int encoder_aac::add_adts_header(AVPacket *packet)
+{
+    if (!packet || !packet->data || packet->size <= 0) {
+        al_error("Invalid packet for ADTS header addition");
+        return AE_INVALID_CONTEXT;
+    }
+
+    // ADTS头部长度为7字节
+    const int adts_header_size = 7;
+    int new_size = packet->size + adts_header_size;
+    
+    // 创建新的缓冲区
+    uint8_t *new_data = (uint8_t*)av_malloc(new_size);
+    if (!new_data) {
+        al_error("Failed to allocate memory for ADTS header");
+        return AE_FFMPEG_ALLOC_FRAME_FAILED;
+    }
+    
+    // 生成ADTS头部
+    uint8_t adts_header[7];
+    
+    // 获取编码器参数
+    int sample_rate = _encoder_ctx->sample_rate;
+    int channels = ffmpeg_get_codec_context_channels(_encoder_ctx);
+    int profile = _encoder_ctx->profile; // AAC-LC = 1
+    
+    // 采样率索引映射
+    int freq_idx = 4; // 默认44100Hz
+    switch (sample_rate) {
+        case 96000: freq_idx = 0; break;
+        case 88200: freq_idx = 1; break;
+        case 64000: freq_idx = 2; break;
+        case 48000: freq_idx = 3; break;
+        case 44100: freq_idx = 4; break;
+        case 32000: freq_idx = 5; break;
+        case 24000: freq_idx = 6; break;
+        case 22050: freq_idx = 7; break;
+        case 16000: freq_idx = 8; break;
+        case 12000: freq_idx = 9; break;
+        case 11025: freq_idx = 10; break;
+        case 8000: freq_idx = 11; break;
+        default: freq_idx = 4; break;
+    }
+    
+    // 声道配置
+    int channel_config = channels;
+    if (channels > 6) channel_config = 0; // 自定义配置
+    
+    // 构建ADTS头部
+    adts_header[0] = 0xFF; // syncword (12 bits) - 高8位
+    adts_header[1] = 0xF0 | (0 << 3) | (0 << 2) | 1; // syncword低4位 + MPEG版本 + Layer + protection_absent
+    adts_header[2] = ((profile - 1) << 6) | (freq_idx << 2) | (0 << 1) | ((channel_config >> 2) & 1);
+    adts_header[3] = ((channel_config & 3) << 6) | (0 << 5) | (0 << 4) | (0 << 3) | ((new_size >> 11) & 3);
+    adts_header[4] = (new_size >> 3) & 0xFF;
+    adts_header[5] = ((new_size & 7) << 5) | 0x1F;
+    adts_header[6] = 0xFC; // number_of_raw_data_blocks_in_frame (2 bits) + 6位填充
+    
+    // 复制ADTS头部和原始数据
+    memcpy(new_data, adts_header, adts_header_size);
+    memcpy(new_data + adts_header_size, packet->data, packet->size);
+    
+    // 释放原始数据并更新packet
+    av_packet_unref(packet);
+    packet->data = new_data;
+    packet->size = new_size;
+    
+    al_debug("ADTS header added: total_size=%d, profile=%d, freq_idx=%d, channels=%d", 
+             new_size, profile, freq_idx, channels);
+    
+    return AE_NO;
+}
+
 } // namespace am

+ 6 - 0
libs/Recorder/encoder_aac.h

@@ -51,6 +51,9 @@ private:
     void encode_loop();
 
     void cleanup();
+    
+    // 为raw AAC数据添加ADTS头部
+    int add_adts_header(AVPacket *packet);
 
 private:
     cb_aac_data _on_data;
@@ -69,6 +72,9 @@ private:
     uint8_t *_buff;
     int _buff_size;
 
+    // BSF for converting raw AAC to ADTS format
+    AVBSFContext *_bsf_ctx;
+
     std::mutex _mutex;
     std::condition_variable _cond_var;
     bool _cond_notify;

+ 56 - 0
libs/Recorder/ffmpeg_compat.h

@@ -480,3 +480,59 @@ inline AVBitStreamFilterContext* ffmpeg_bitstream_filter_init(const char* name)
     return av_bitstream_filter_init(name);
 #endif
 }
+
+inline void ffmpeg_bitstream_filter_close(AVBitStreamFilterContext* ctx) {
+#if FFMPEG_VERSION_MAJOR >= 6
+    if (ctx) {
+        av_bsf_free(&ctx);
+    }
+#else
+    if (ctx) {
+        av_bitstream_filter_close(ctx);
+    }
+#endif
+}
+
+inline int ffmpeg_bitstream_filter_filter(AVBitStreamFilterContext* ctx, AVPacket* packet) {
+#if FFMPEG_VERSION_MAJOR >= 6
+    if (!ctx || !packet) return -1;
+    
+    // 对于新版本FFmpeg,需要先初始化BSF上下文
+    if (ctx->par_in->codec_type == AVMEDIA_TYPE_UNKNOWN) {
+        // 从packet推断参数
+        ctx->par_in->codec_type = AVMEDIA_TYPE_AUDIO;
+        ctx->par_in->codec_id = AV_CODEC_ID_AAC;
+        
+        if (avcodec_parameters_copy(ctx->par_out, ctx->par_in) < 0) {
+            return -1;
+        }
+        
+        if (av_bsf_init(ctx) < 0) {
+            return -1;
+        }
+    }
+    
+    int ret = av_bsf_send_packet(ctx, packet);
+    if (ret < 0) return ret;
+    
+    ret = av_bsf_receive_packet(ctx, packet);
+    return ret;
+#else
+    if (!ctx || !packet) return -1;
+    
+    uint8_t* output_data = nullptr;
+    int output_size = 0;
+    
+    int ret = av_bitstream_filter_filter(ctx, nullptr, nullptr, &output_data, &output_size,
+                                         packet->data, packet->size, packet->flags & AV_PKT_FLAG_KEY);
+    
+    if (ret >= 0 && output_data && output_size > 0) {
+        av_packet_unref(packet);
+        packet->data = output_data;
+        packet->size = output_size;
+        packet->buf = av_buffer_create(output_data, output_size, av_buffer_default_free, nullptr, 0);
+    }
+    
+    return ret;
+#endif
+}

+ 14 - 1
libs/Recorder/log_helper.h

@@ -7,6 +7,8 @@
 
 #include <windows.h>
 
+#include <QDebug>
+
 class AMLog
 {
 public:
@@ -60,7 +62,18 @@ static const char* AM_LOG_STR[] = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
         strftime(datetime_str, 20, "%Y-%m-%d %H:%M:%S", &today); \
         AMLog* am_log = AMLog::get(); \
         if (am_log) { \
-            am_log->PRINT_LINE(AM_LOG_STR[type], format, datetime_str, now.millitm, ##__VA_ARGS__); \
+            char log_buffer[1024]; \
+            sprintf_s(log_buffer, \
+                      sizeof(log_buffer), \
+                      "%s-%.3d [%s] [%s(%d)] " format "\n", \
+                      datetime_str, \
+                      now.millitm, \
+                      AM_LOG_STR[type], \
+                      __FUNCTION__, \
+                      __LINE__, \
+                      ##__VA_ARGS__); \
+            am_log->printf("%s", log_buffer); \
+            qDebug() << QString::fromUtf8(log_buffer); \
         } else { \
             al_printf(AM_LOG_STR[type], format, datetime_str, now.millitm, ##__VA_ARGS__); \
         } \

+ 120 - 19
libs/Recorder/muxer_ffmpeg.cpp

@@ -454,7 +454,14 @@ int muxer_ffmpeg::alloc_oc(const char *output_file, const MUX_SETTING_T &setting
     int ret = 0;
 
     do {
-        ret = avformat_alloc_output_context2(&_fmt_ctx, NULL, NULL, output_file);
+        // 检查是否为RTMP流,如果是则指定flv格式
+        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";
+        }
+        
+        ret = avformat_alloc_output_context2(&_fmt_ctx, NULL, format_name, output_file);
         if (ret < 0 || !_fmt_ctx) {
             error = AE_FFMPEG_ALLOC_CONTEXT_FAILED;
             break;
@@ -740,20 +747,39 @@ int muxer_ffmpeg::add_audio_stream(const MUX_SETTING_T &setting,
         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));
 
-        if (_fmt_ctx->oformat->flags
-            & AVFMT_GLOBALHEADER) { //without this,normal player can not play
-            codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
-
-            codec_ctx->extradata_size
-                = _a_stream->a_enc->get_extradata_size(); // +AV_INPUT_BUFFER_PADDING_SIZE;
-            codec_ctx->extradata = (uint8_t *) av_memdup(_a_stream->a_enc->get_extradata(),
-                                                         _a_stream->a_enc->get_extradata_size());
+        // 检查是否为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");
+            }
         }
 
         _a_stream->st = st;
 
         _a_stream->setting = setting;
-        _a_stream->filter = ffmpeg_bitstream_filter_init("aac_adtstoasc");
+        // 对于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),使用aac_adtstoasc过滤器
+            _a_stream->filter = ffmpeg_bitstream_filter_init("aac_adtstoasc");
+            al_debug("Non-RTMP output, using aac_adtstoasc filter");
+        }
 
     } while (0);
 
@@ -817,6 +843,12 @@ void muxer_ffmpeg::cleanup_audio()
 
     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++) {
@@ -938,21 +970,90 @@ int muxer_ffmpeg::write_audio(AVPacket *packet)
 			_v_stream->pre_pts = packet->pts;
 		}*/
 
-    // 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
-    packet->pts = packet->pts - _base_time;
-    packet->dts = packet->pts; //make sure that dts is equal to 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);
 
-    av_assert0(packet->data != NULL);
+    // 验证音频包的基本有效性
+    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]);
+    }
+    
+    // 应用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,
-                                         packet); //no need to unref packet,this will be auto unref
+    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);
     }

+ 473 - 440
libs/Recorder/test_main.cpp

@@ -1,491 +1,524 @@
-// #include "device_audios.h"
+#include "device_audios.h"
 
-// #include "record_audio_factory.h"
-// #include "record_desktop_factory.h"
-// #include "headers_mmdevice.h"
+#include "headers_mmdevice.h"
+#include "record_audio_factory.h"
+#include "record_desktop_factory.h"
 
-// #include "encoder_aac.h"
-// #include "resample_pcm.h"
-// #include "filter_aresample.h"
+#include "encoder_aac.h"
+#include "filter_aresample.h"
+#include "resample_pcm.h"
 
-// #include "muxer_define.h"
-// #include "muxer_ffmpeg.h"
-
-// #include "utils_string.h"
-// #include "system_version.h"
-// #include "error_define.h"
-// #include "log_helper.h"
-// #include "hardware_acceleration.h"
+#include "muxer_define.h"
+#include "muxer_ffmpeg.h"
 
-// #include "remuxer_ffmpeg.h"
-// #include "headers_ffmpeg.h"
-
-// #include <stdio.h>
-// #include <stdlib.h>
-// #include <stddef.h>
-// #include <stdint.h>
+#include "error_define.h"
+#include "hardware_acceleration.h"
+#include "log_helper.h"
+#include "system_version.h"
+#include "utils_string.h"
 
-// #define USE_WASAPI 1
+#include "headers_ffmpeg.h"
+#include "remuxer_ffmpeg.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define USE_WASAPI 1
+
+#define V_FRAME_RATE 30
+#define V_BIT_RATE 1280 * 1000
+#define V_WIDTH GetSystemMetrics(SM_CXSCREEN)
+#define V_HEIGHT GetSystemMetrics(SM_CYSCREEN);
+#define V_QUALITY 100
+
+#define A_SAMPLE_CHANNEL 2
+#define A_SAMPLE_RATE 44100
+#define A_BIT_RATE 128000
 
-// #define V_FRAME_RATE 30
-// #define V_BIT_RATE 1280*1000
-// #define V_WIDTH GetSystemMetrics(SM_CXSCREEN)
-// #define V_HEIGHT  GetSystemMetrics(SM_CYSCREEN);
-// #define V_QUALITY 100
+//for test muxer
+static am::record_audio *_recorder_speaker = nullptr;
+static am::record_audio *_recorder_microphone = nullptr;
+static am::record_desktop *_recorder_desktop = nullptr;
+static am::muxer_file *_muxer;
+static am::record_audio *audios[2];
 
-// #define A_SAMPLE_CHANNEL 2
-// #define A_SAMPLE_RATE 44100
-// #define A_BIT_RATE 128000
+//for test audio record
+static am::record_audio *_recorder_audio = nullptr;
+static am::encoder_aac *_encoder_aac = nullptr;
+static am::resample_pcm *_resample_pcm = nullptr;
+static am::filter_aresample *_filter_aresample = nullptr;
 
-// //for test muxer
-// static am::record_audio *_recorder_speaker = nullptr;
-// static am::record_audio *_recorder_microphone = nullptr;
-// static am::record_desktop *_recorder_desktop = nullptr;
-// static am::muxer_file *_muxer;
-// static am::record_audio *audios[2];
+static int _sample_in = 0;
+static int _sample_size = 0;
+static int _resample_size = 0;
+static uint8_t *_sample_buffer = nullptr;
+static uint8_t *_resample_buffer = nullptr;
 
-// //for test audio record
-// static am::record_audio *_recorder_audio = nullptr;
-// static am::encoder_aac *_encoder_aac = nullptr;
-// static am::resample_pcm *_resample_pcm = nullptr;
-// static am::filter_aresample *_filter_aresample = nullptr;
+int start_muxer()
+{
+    std::string input_id, input_name, out_id, out_name;
 
-// static int _sample_in = 0;
-// static int _sample_size = 0;
-// static int _resample_size = 0;
-// static uint8_t *_sample_buffer = nullptr;
-// static uint8_t *_resample_buffer = nullptr;
+    am::device_audios::get_default_input_device(input_id, input_name);
 
-// int start_muxer() {
-// 	std::string input_id, input_name, out_id, out_name;
+    al_info("use default input aduio device:%s", input_name.c_str());
 
-// 	am::device_audios::get_default_input_device(input_id, input_name);
+    am::device_audios::get_default_ouput_device(out_id, out_name);
 
-// 	al_info("use default input aduio device:%s", input_name.c_str());
+    al_info("use default output aduio device:%s", out_name.c_str());
 
-// 	am::device_audios::get_default_ouput_device(out_id, out_name);
+    //first audio resrouce must be speaker,otherwise the audio pts may be not correct,may need to change the filter amix descriptions with duration & sync option
+#if !USE_WASAPI
 
-// 	al_info("use default output aduio device:%s", out_name.c_str());
+    record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_DSHOW, &_recorder_speaker);
+    _recorder_speaker->init(am::utils_string::ascii_utf8("audio=virtual-audio-capturer"),
+                            am::utils_string::ascii_utf8("audio=virtual-audio-capturer"),
+                            false);
 
-// 	//first audio resrouce must be speaker,otherwise the audio pts may be not correct,may need to change the filter amix descriptions with duration & sync option
-// #if !USE_WASAPI
+    record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_DSHOW, &_recorder_microphone);
+    _recorder_microphone->init(am::utils_string::ascii_utf8("audio=") + input_name, input_id, true);
+#else
+    record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_speaker);
+    _recorder_speaker->init(out_name, out_id, false);
 
-// 	record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_DSHOW, &_recorder_speaker);
-// 	_recorder_speaker->init(
-// 		am::utils_string::ascii_utf8("audio=virtual-audio-capturer"),
-// 		am::utils_string::ascii_utf8("audio=virtual-audio-capturer"),
-// 		false
-// 	);
+    record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_microphone);
+    _recorder_microphone->init(input_name, input_id, true);
+#endif // !USE_WASAPI
 
-// 	record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_DSHOW, &_recorder_microphone);
-// 	_recorder_microphone->init(am::utils_string::ascii_utf8("audio=") + input_name, input_id, true);
-// #else
-// 	record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_speaker);
-// 	_recorder_speaker->init(out_name, out_id, false);
+    record_desktop_new(RECORD_DESKTOP_TYPES::DT_DESKTOP_WIN_DUPLICATION, &_recorder_desktop);
 
-// 	record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_microphone);
-// 	_recorder_microphone->init(input_name, input_id, true);
-// #endif // !USE_WASAPI
+    RECORD_DESKTOP_RECT rect;
+    rect.left = 0;
+    rect.top = 0;
+    rect.right = V_WIDTH;
+    rect.bottom = V_HEIGHT;
 
-// 	record_desktop_new(RECORD_DESKTOP_TYPES::DT_DESKTOP_WIN_DUPLICATION, &_recorder_desktop);
+    _recorder_desktop->init(rect, V_FRAME_RATE);
 
-// 	RECORD_DESKTOP_RECT rect;
-// 	rect.left = 0;
-// 	rect.top = 0;
-// 	rect.right = V_WIDTH;
-// 	rect.bottom = V_HEIGHT;
+    audios[0] = _recorder_microphone;
+    audios[1] = _recorder_speaker;
 
-// 	_recorder_desktop->init(rect, V_FRAME_RATE);
+    _muxer = new am::muxer_ffmpeg();
 
-// 	audios[0] = _recorder_microphone;
-// 	audios[1] = _recorder_speaker;
+    am::MUX_SETTING setting;
+    setting.v_frame_rate = V_FRAME_RATE;
+    setting.v_bit_rate = V_BIT_RATE;
+    setting.v_width = V_WIDTH;
+    setting.v_height = V_HEIGHT;
+    setting.v_qb = V_QUALITY;
+    setting.v_encoder_id = am::EID_VIDEO_X264;
+    setting.v_out_width = 1920;
+    setting.v_out_height = 1080;
 
-// 	_muxer = new am::muxer_ffmpeg();
-
-// 	am::MUX_SETTING setting;
-// 	setting.v_frame_rate = V_FRAME_RATE;
-// 	setting.v_bit_rate = V_BIT_RATE;
-// 	setting.v_width = V_WIDTH;
-// 	setting.v_height = V_HEIGHT;
-// 	setting.v_qb = V_QUALITY;
-// 	setting.v_encoder_id = am::EID_VIDEO_X264;
-// 	setting.v_out_width = 1920;
-// 	setting.v_out_height = 1080;
-
-// 	setting.a_nb_channel = A_SAMPLE_CHANNEL;
-// 	setting.a_sample_fmt = AV_SAMPLE_FMT_FLTP;
-// 	setting.a_sample_rate = A_SAMPLE_RATE;
-// 	setting.a_bit_rate = A_BIT_RATE;
+    setting.a_nb_channel = A_SAMPLE_CHANNEL;
+    setting.a_sample_fmt = AV_SAMPLE_FMT_FLTP;
+    setting.a_sample_rate = A_SAMPLE_RATE;
+    setting.a_bit_rate = A_BIT_RATE;
+
+    int error = _muxer->init(am::utils_string::ascii_utf8("..\\..\\save.mp4").c_str(),
+                             _recorder_desktop,
+                             audios,
+                             2,
+                             setting);
+    if (error != AE_NO) {
+        return error;
+    }
+
+    _muxer->start();
+
+    return error;
+}
+
+void stop_muxer()
+{
+    _muxer->stop();
+
+    delete _recorder_desktop;
+
+    if (_recorder_speaker)
+        delete _recorder_speaker;
+
+    if (_recorder_microphone)
+        delete _recorder_microphone;
+
+    delete _muxer;
+}
+
+void test_recorder()
+{
+    //av_log_set_level(AV_LOG_DEBUG);
+
+    start_muxer();
+
+    getchar();
+
+    //stop have bug that sometime will stuck
+    stop_muxer();
+
+    al_info("record stoped...");
+}
+
+void show_devices()
+{
+    std::list<am::DEVICE_AUDIOS> devices;
+
+    am::device_audios::get_input_devices(devices);
+
+    for (auto device : devices) {
+        al_info("audio input name:%s id:%s", device.name.c_str(), device.id.c_str());
+    }
+
+    am::device_audios::get_output_devices(devices);
+
+    for (auto device : devices) {
+        al_info("audio output name:%s id:%s", device.name.c_str(), device.id.c_str());
+    }
+}
+
+void on_aac_data(AVPacket *packet)
+{
+    al_debug("on aac data :%d", packet->size);
+}
+
+void on_aac_error(int) {}
+
+void on_pcm_error(int, int) {}
+
+void on_pcm_data1(AVFrame *frame, int index)
+{
+    int copied_len = 0;
+    int sample_len = av_samples_get_buffer_size(NULL,
+                                                ffmpeg_get_frame_channels(frame),
+                                                frame->nb_samples,
+                                                (AVSampleFormat) frame->format,
+                                                1);
+    int remain_len = sample_len;
+
+    int is_planner = av_sample_fmt_is_planar((AVSampleFormat) frame->format);
+
+    while (remain_len > 0) { //should add is_planner codes
+                             //cache pcm
+        copied_len = min(_sample_size - _sample_in, remain_len);
+        if (copied_len) {
+            memcpy(_sample_buffer + _sample_in,
+                   frame->data[0] + sample_len - remain_len,
+                   copied_len);
+            _sample_in += copied_len;
+            remain_len = remain_len - copied_len;
+        }
+
+        //got enough pcm to encoder,resample and mix
+        if (_sample_in == _sample_size) {
+            int ret = _resample_pcm->convert(_sample_buffer,
+                                             _sample_size,
+                                             _resample_buffer,
+                                             _resample_size);
+            if (ret > 0) {
+                _encoder_aac->put(_resample_buffer, _resample_size, frame);
+            } else {
+                al_debug("resample audio %d failed,%d", index, ret);
+            }
+
+            _sample_in = 0;
+        }
+    }
+}
+
+void on_pcm_data(AVFrame *frame, int index)
+{
+    _filter_aresample->add_frame(frame);
+}
+
+void on_aresample_data(AVFrame *frame, int index)
+{
+    int copied_len = 0;
+    int sample_len = av_samples_get_buffer_size(frame->linesize,
+                                                ffmpeg_get_frame_channels(frame),
+                                                frame->nb_samples,
+                                                (AVSampleFormat) frame->format,
+                                                1);
+    sample_len = av_samples_get_buffer_size(NULL,
+                                            ffmpeg_get_frame_channels(frame),
+                                            frame->nb_samples,
+                                            (AVSampleFormat) frame->format,
+                                            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(_sample_size - _sample_in, remain_len);
+            if (copied_len) {
+                memcpy(_resample_buffer + _sample_in,
+                       frame->data[0] + sample_len - remain_len,
+                       copied_len);
+                _sample_in += copied_len;
+                remain_len = remain_len - copied_len;
+            }
+
+            //got enough pcm to encoder,resample and mix
+            if (_sample_in == _sample_size) {
+                _encoder_aac->put(_resample_buffer, _sample_size, frame);
+
+                _sample_in = 0;
+            }
+        }
+    } else { //resample size is channels*frame->linesize[0],for 2 channels
+        while (remain_len > 0) {
+            copied_len = min(_sample_size - _sample_in, remain_len);
+            if (copied_len) {
+                memcpy(_resample_buffer + _sample_in / 2,
+                       frame->data[0] + (sample_len - remain_len) / 2,
+                       copied_len / 2);
+                memcpy(_resample_buffer + _sample_size / 2 + _sample_in / 2,
+                       frame->data[1] + (sample_len - remain_len) / 2,
+                       copied_len / 2);
+                _sample_in += copied_len;
+                remain_len = remain_len - copied_len;
+            }
+
+            if (_sample_in == _sample_size) {
+                _encoder_aac->put(_resample_buffer, _sample_size, frame);
+
+                _sample_in = 0;
+            }
+        }
+    }
+}
+
+void on_aresample_error(int error, int index) {}
+
+void save_aac()
+{
+    std::string input_id, input_name, out_id, out_name;
+
+    am::device_audios::get_default_input_device(input_id, input_name);
+
+    am::device_audios::get_default_ouput_device(out_id, out_name);
+
+#if 0
+	al_info("use default \r\noutput aduio device name:%s \r\noutput audio device id:%s ",
+		out_name.c_str(), out_id.c_str());
+
+	record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_audio);
+	_recorder_audio->init(out_name, out_id, false);
+#else
+    al_info("use default \r\ninput aduio device name:%s \r\ninput audio device id:%s ",
+            input_name.c_str(),
+            input_id.c_str());
+
+    record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_audio);
+    _recorder_audio->init(input_name, input_id, true);
+#endif
+
+    _recorder_audio->registe_cb(on_pcm_data, on_pcm_error, 0);
+
+    _encoder_aac = new am::encoder_aac();
+    _encoder_aac->init(A_SAMPLE_CHANNEL, A_SAMPLE_RATE, AV_SAMPLE_FMT_FLTP, A_BIT_RATE);
+
+    am::SAMPLE_SETTING src, dst = {0};
+    src = {_encoder_aac->get_nb_samples(),
+           (int64_t) ffmpeg_get_default_channel_layout(_recorder_audio->get_channel_num()),
+           _recorder_audio->get_channel_num(),
+           _recorder_audio->get_fmt(),
+           _recorder_audio->get_sample_rate()};
+    dst = {_encoder_aac->get_nb_samples(),
+           (int64_t) ffmpeg_get_default_channel_layout(A_SAMPLE_CHANNEL),
+           A_SAMPLE_CHANNEL,
+           AV_SAMPLE_FMT_FLTP,
+           A_SAMPLE_RATE};
+
+    _resample_pcm = new am::resample_pcm();
+    _resample_pcm->init(&src, &dst, &_resample_size);
+    _resample_buffer = new uint8_t[_resample_size];
+
+    _sample_size = av_samples_get_buffer_size(NULL,
+                                              A_SAMPLE_CHANNEL,
+                                              _encoder_aac->get_nb_samples(),
+                                              _recorder_audio->get_fmt(),
+                                              1);
+    _sample_buffer = new uint8_t[_sample_size];
+
+    _filter_aresample = new am::filter_aresample();
+    _filter_aresample->init({NULL,
+                             NULL,
+                             _recorder_audio->get_time_base(),
+                             _recorder_audio->get_sample_rate(),
+                             _recorder_audio->get_fmt(),
+                             _recorder_audio->get_channel_num(),
+                             (int64_t) ffmpeg_get_default_channel_layout(
+                                 _recorder_audio->get_channel_num())},
+                            {NULL,
+                             NULL,
+                             {1, AV_TIME_BASE},
+                             A_SAMPLE_RATE,
+                             AV_SAMPLE_FMT_FLTP,
+                             A_SAMPLE_CHANNEL,
+                             (int64_t) ffmpeg_get_default_channel_layout(A_SAMPLE_CHANNEL)},
+                            0);
+    _filter_aresample->registe_cb(on_aresample_data, on_aresample_error);
+
+    _filter_aresample->start();
+    _encoder_aac->start();
+    _recorder_audio->start();
+
+    getchar();
+
+    _recorder_audio->stop();
+    _filter_aresample->stop();
+
+    delete _recorder_audio;
+    delete _encoder_aac;
+    delete _resample_pcm;
+    delete _filter_aresample;
+
+    delete[] _sample_buffer;
+    delete[] _resample_buffer;
+}
+
+void test_audio()
+{
+    std::string input_id, input_name, out_id, out_name;
+
+    am::device_audios::get_default_input_device(input_id, input_name);
+
+    al_info("use default \r\ninput aduio device name:%s \r\ninput audio device id:%s ",
+            input_name.c_str(),
+            input_id.c_str());
+
+    am::device_audios::get_default_ouput_device(out_id, out_name);
+
+    al_info("use default \r\noutput aduio device name:%s \r\noutput audio device id:%s ",
+            out_name.c_str(),
+            out_id.c_str());
+
+    record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_speaker);
+    _recorder_speaker->init(am::utils_string::ascii_utf8("Default"),
+                            am::utils_string::ascii_utf8("Default"),
+                            false);
+
+    //record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_microphone);
+    //_recorder_microphone->init(input_name,input_id, true);
+
+    _recorder_speaker->start();
+
+    //_recorder_microphone->start();
+
+    getchar();
+
+    _recorder_speaker->stop();
+    //_recorder_microphone->stop();
+}
+
+void on_remux_progress(const char *src, int progress, int total)
+{
+    al_debug("on remux progress:%s %d %d", src, progress, total);
+}
+
+void on_remux_state(const char *src, int state, int error)
+{
+    al_debug("on remux state:%s %d %d", src, state, error);
+}
+
+void test_remux()
+{
+#if TEST_MULTI_THREAD
+    for (int i = 0; i < 20; i++) {
+        am::REMUXER_PARAM param = {0};
+        sprintf_s(param.src, 260, "%d", i);
+        am::remuxer_ffmpeg::instance()->create_remux(param);
+    }
+#else
+    am::REMUXER_PARAM param = {0};
+
+    sprintf_s(param.src, 260, "%s", am::utils_string::ascii_utf8("..\\..\\save.mkv").c_str());
+    sprintf_s(param.dst, 260, "%s", am::utils_string::ascii_utf8("..\\..\\save.mp4").c_str());
+
+    param.cb_progress = on_remux_progress;
+
+    param.cb_state = on_remux_state;
+
+    am::remuxer_ffmpeg::instance()->create_remux(param);
+#endif
+}
+
+void test_scale()
+{
+    static const double scaled_vals[]
+        = {1.0, 1.25, (1.0 / 0.75), 1.5, (1.0 / 0.6), 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 0.0};
 
-// 	int error = _muxer->init(am::utils_string::ascii_utf8("..\\..\\save.mp4").c_str(), _recorder_desktop, audios, 2, setting);
-// 	if (error != AE_NO) {
-// 		return error;
-// 	}
-
-// 	_muxer->start();
+    auto get_valid_out_resolution =
+        [](int src_width, int src_height, int *out_width, int *out_height) {
+            int scale_cx = src_width;
+            int scale_cy = src_height;
 
-// 	return error;
-// }
-
-// void stop_muxer()
-// {
-// 	_muxer->stop();
-
-// 	delete _recorder_desktop;
-
-// 	if(_recorder_speaker)
-// 		delete _recorder_speaker;
-
-// 	if (_recorder_microphone)
-// 		delete _recorder_microphone;
-
-// 	delete _muxer;
-// }
-
-// void test_recorder()
-// {
-// 	//av_log_set_level(AV_LOG_DEBUG);
-
-// 	start_muxer();
+            int i = 0;
 
-// 	getchar();
+            while (((scale_cx * scale_cy) > (1920 * 1080)) && scaled_vals[i] > 0.0) {
+                double scale = scaled_vals[i++];
+                scale_cx = uint32_t(double(src_width) / scale);
+                scale_cy = uint32_t(double(src_height) / scale);
+            }
 
-// 	//stop have bug that sometime will stuck
-// 	stop_muxer();
+            if (scale_cx % 2 != 0) {
+                scale_cx += 1;
+            }
 
-// 	al_info("record stoped...");
-// }
-
-// void show_devices()
-// {
-// 	std::list<am::DEVICE_AUDIOS> devices;
+            if (scale_cy % 2 != 0) {
+                scale_cy += 1;
+            }
 
-// 	am::device_audios::get_input_devices(devices);
+            *out_width = scale_cx;
+            *out_height = scale_cy;
 
-// 	for each (auto device in devices)
-// 	{
-// 		al_info("audio input name:%s id:%s", device.name.c_str(), device.id.c_str());
-// 	}
+            al_info("get valid output resolution from %dx%d to %dx%d,with scale:%lf",
+                    src_width,
+                    src_height,
+                    scale_cx,
+                    scale_cy,
+                    scaled_vals[i]);
+        };
 
-// 	am::device_audios::get_output_devices(devices);
-
-// 	for each (auto device in devices)
-// 	{
-// 		al_info("audio output name:%s id:%s", device.name.c_str(), device.id.c_str());
-// 	}
-// }
-
-// void on_aac_data(AVPacket *packet) {
-// 	al_debug("on aac data :%d", packet->size);
-// }
-
-// void on_aac_error(int) {
-
-// }
-
-// void on_pcm_error(int, int) {
-
-// }
-
-// void on_pcm_data1(AVFrame *frame, int index) {
-
-// 	int copied_len = 0;
-// 	int sample_len = av_samples_get_buffer_size(NULL, ffmpeg_get_frame_channels(frame), frame->nb_samples, (AVSampleFormat)frame->format, 1);
-// 	int remain_len = sample_len;
-
-// 	int is_planner = av_sample_fmt_is_planar((AVSampleFormat)frame->format);
-
-// 	while (remain_len > 0) {//should add is_planner codes
-// 							//cache pcm
-// 		copied_len = min(_sample_size - _sample_in, remain_len);
-// 		if (copied_len) {
-// 			memcpy(_sample_buffer + _sample_in, frame->data[0] + sample_len - remain_len, copied_len);
-// 			_sample_in += copied_len;
-// 			remain_len = remain_len - copied_len;
-// 		}
-
-// 		//got enough pcm to encoder,resample and mix
-// 		if (_sample_in == _sample_size) {
-// 			int ret = _resample_pcm->convert(_sample_buffer, _sample_size, _resample_buffer,_resample_size);
-// 			if (ret > 0) {
-// 				_encoder_aac->put(_resample_buffer, _resample_size, frame);
-// 			}
-// 			else {
-// 				al_debug("resample audio %d failed,%d", index, ret);
-// 			}
-
-// 			_sample_in = 0;
-// 		}
-// 	}
-// }
-
-// void on_pcm_data(AVFrame *frame, int index) {
-// 	_filter_aresample->add_frame(frame);
-// }
-
-// void on_aresample_data(AVFrame * frame,int index) {
-// 	int copied_len = 0;
-// 	int sample_len = av_samples_get_buffer_size(frame->linesize, ffmpeg_get_frame_channels(frame), frame->nb_samples, (AVSampleFormat)frame->format, 1);
-// 	sample_len = av_samples_get_buffer_size(NULL, ffmpeg_get_frame_channels(frame), frame->nb_samples, (AVSampleFormat)frame->format, 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(_sample_size - _sample_in, remain_len);
-// 			if (copied_len) {
-// 				memcpy(_resample_buffer + _sample_in, frame->data[0] + sample_len - remain_len, copied_len);
-// 				_sample_in += copied_len;
-// 				remain_len = remain_len - copied_len;
-// 			}
-
-// 			//got enough pcm to encoder,resample and mix
-// 			if (_sample_in == _sample_size) {
-// 				_encoder_aac->put(_resample_buffer, _sample_size, frame);
-
-// 				_sample_in = 0;
-// 			}
-// 		}
-// 	}
-// 	else {//resample size is channels*frame->linesize[0],for 2 channels
-// 		while (remain_len > 0) {
-// 			copied_len = min(_sample_size - _sample_in, remain_len);
-// 			if (copied_len) {
-// 				memcpy(_resample_buffer + _sample_in / 2, frame->data[0] + (sample_len - remain_len) / 2, copied_len / 2);
-// 				memcpy(_resample_buffer + _sample_size / 2 + _sample_in / 2, frame->data[1] + (sample_len - remain_len) / 2, copied_len / 2);
-// 				_sample_in += copied_len;
-// 				remain_len = remain_len - copied_len;
-// 			}
-
-// 			if (_sample_in == _sample_size) {
-// 				_encoder_aac->put(_resample_buffer, _sample_size, frame);
-
-// 				_sample_in = 0;
-// 			}
-// 		}
-// 	}
-// }
-
-// void on_aresample_error(int error,int index) {
-
-// }
-
-// void save_aac() {
-// 	std::string input_id, input_name, out_id, out_name;
-
-// 	am::device_audios::get_default_input_device(input_id, input_name);
-
-// 	am::device_audios::get_default_ouput_device(out_id, out_name);
-
-// #if 0
-// 	al_info("use default \r\noutput aduio device name:%s \r\noutput audio device id:%s ",
-// 		out_name.c_str(), out_id.c_str());
-
-// 	record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_audio);
-// 	_recorder_audio->init(out_name, out_id, false);
-// #else
-// 	al_info("use default \r\ninput aduio device name:%s \r\ninput audio device id:%s ",
-// 		input_name.c_str(), input_id.c_str());
-
-// 	record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_audio);
-// 	_recorder_audio->init(input_name, input_id, true);
-// #endif
-
-// 	_recorder_audio->registe_cb(on_pcm_data, on_pcm_error, 0);
-
-// 	_encoder_aac = new am::encoder_aac();
-// 	_encoder_aac->init(A_SAMPLE_CHANNEL, A_SAMPLE_RATE, AV_SAMPLE_FMT_FLTP, A_BIT_RATE);
-
-// 	am::SAMPLE_SETTING src, dst = { 0 };
-// 	src = {
-// 		_encoder_aac->get_nb_samples(),
-// 		(int64_t)ffmpeg_get_default_channel_layout(_recorder_audio->get_channel_num()),
-// 		_recorder_audio->get_channel_num(),
-// 		_recorder_audio->get_fmt(),
-// 		_recorder_audio->get_sample_rate()
-// 	};
-// 	dst = {
-// 		_encoder_aac->get_nb_samples(),
-// 		(int64_t)ffmpeg_get_default_channel_layout(A_SAMPLE_CHANNEL),
-// 		A_SAMPLE_CHANNEL,
-// 		AV_SAMPLE_FMT_FLTP,
-// 		A_SAMPLE_RATE
-// 	};
-
-// 	_resample_pcm = new am::resample_pcm();
-// 	_resample_pcm->init(&src, &dst, &_resample_size);
-// 	_resample_buffer = new uint8_t[_resample_size];
-
-// 	_sample_size = av_samples_get_buffer_size(NULL, A_SAMPLE_CHANNEL, _encoder_aac->get_nb_samples(), _recorder_audio->get_fmt(), 1);
-// 	_sample_buffer = new uint8_t[_sample_size];
-
-// 	_filter_aresample = new am::filter_aresample();
-// 	_filter_aresample->init({
-// 		NULL,NULL,
-// 		_recorder_audio->get_time_base(),
-// 		_recorder_audio->get_sample_rate(),
-// 		_recorder_audio->get_fmt(),
-// 		_recorder_audio->get_channel_num(),
-// 		(int64_t)ffmpeg_get_default_channel_layout(_recorder_audio->get_channel_num())
-// 	}, {
-// 		NULL,NULL,
-// 		{ 1,AV_TIME_BASE },
-// 		A_SAMPLE_RATE,
-// 		AV_SAMPLE_FMT_FLTP,
-// 		A_SAMPLE_CHANNEL,
-// 		(int64_t)ffmpeg_get_default_channel_layout(A_SAMPLE_CHANNEL)
-// 	},0);
-// 	_filter_aresample->registe_cb(on_aresample_data, on_aresample_error);
-
-// 	_filter_aresample->start();
-// 	_encoder_aac->start();
-// 	_recorder_audio->start();
-
-// 	getchar();
-
-// 	_recorder_audio->stop();
-// 	_filter_aresample->stop();
-
-// 	delete _recorder_audio;
-// 	delete _encoder_aac;
-// 	delete _resample_pcm;
-// 	delete _filter_aresample;
-
-// 	delete[] _sample_buffer;
-// 	delete[] _resample_buffer;
-// }
+    int src_width = 2736, src_height = 1824;
+    int dst_width, dst_height;
 
-// void test_audio()
-// {
-// 	std::string input_id, input_name, out_id, out_name;
-
-// 	am::device_audios::get_default_input_device(input_id, input_name);
-
-// 	al_info("use default \r\ninput aduio device name:%s \r\ninput audio device id:%s ",
-// 		input_name.c_str(), input_id.c_str());
-
-// 	am::device_audios::get_default_ouput_device(out_id, out_name);
-
-// 	al_info("use default \r\noutput aduio device name:%s \r\noutput audio device id:%s ",
-// 		out_name.c_str(), out_id.c_str());
-
-// 	record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_speaker);
-// 	_recorder_speaker->init(am::utils_string::ascii_utf8("Default"), am::utils_string::ascii_utf8("Default"), false);
-
-// 	//record_audio_new(RECORD_AUDIO_TYPES::AT_AUDIO_WAS, &_recorder_microphone);
-// 	//_recorder_microphone->init(input_name,input_id, true);
-
-// 	_recorder_speaker->start();
-
-// 	//_recorder_microphone->start();
-
-// 	getchar();
-
-// 	_recorder_speaker->stop();
-// 	//_recorder_microphone->stop();
-// }
-
-// void on_remux_progress(const char *src, int progress, int total)
-// {
-// 	al_debug("on remux progress:%s %d %d", src, progress, total);
-// }
-
-// void on_remux_state(const char *src, int state, int error) {
-// 	al_debug("on remux state:%s %d %d", src, state, error);
-// }
-
-// void test_remux() {
-// #if TEST_MULTI_THREAD
-// 	for (int i = 0; i < 20; i++) {
-// 		am::REMUXER_PARAM param = { 0 };
-// 		sprintf_s(param.src, 260, "%d", i);
-// 		am::remuxer_ffmpeg::instance()->create_remux(param);
-// 	}
-// #else
-// 	am::REMUXER_PARAM param = { 0 };
-
-// 	sprintf_s(param.src, 260, "%s", am::utils_string::ascii_utf8("..\\..\\save.mkv").c_str());
-// 	sprintf_s(param.dst, 260, "%s", am::utils_string::ascii_utf8("..\\..\\save.mp4").c_str());
-
-// 	param.cb_progress = on_remux_progress;
-
-// 	param.cb_state = on_remux_state;
-
-// 	am::remuxer_ffmpeg::instance()->create_remux(param);
-// #endif
-// }
-
-// void test_scale() {
-// 	static const double scaled_vals[] = { 1.0,         1.25, (1.0 / 0.75), 1.5,
-// 		(1.0 / 0.6), 1.75, 2.0,          2.25,
-// 		2.5,         2.75, 3.0,          0.0 };
-
-// 	auto get_valid_out_resolution = [](int src_width, int src_height, int * out_width, int * out_height)
-// 	{
-// 		int scale_cx = src_width;
-// 		int scale_cy = src_height;
-
-// 		int i = 0;
-
-// 		while (((scale_cx * scale_cy) > (1920 * 1080)) && scaled_vals[i] > 0.0) {
-// 			double scale = scaled_vals[i++];
-// 			scale_cx = uint32_t(double(src_width) / scale);
-// 			scale_cy = uint32_t(double(src_height) / scale);
-// 		}
-
-// 		if (scale_cx % 2 != 0) {
-// 			scale_cx += 1;
-// 		}
-
-// 		if (scale_cy % 2 != 0) {
-// 			scale_cy += 1;
-// 		}
-
-// 		*out_width = scale_cx;
-// 		*out_height = scale_cy;
-
-// 		al_info("get valid output resolution from %dx%d to %dx%d,with scale:%lf", src_width, src_height, scale_cx, scale_cy, scaled_vals[i]);
-// 	};
-
-// 	int src_width=2736, src_height=1824;
-// 	int dst_width, dst_height;
-
-// 	get_valid_out_resolution(src_width, src_height, &dst_width, &dst_height);
-// }
+    get_valid_out_resolution(src_width, src_height, &dst_width, &dst_height);
+}
 
 // int main(int argc, char **argv)
 // {
-// 	al_info("record start...");
+//     al_info("record start...");
 
-// 	am::winversion_info ver = { 0 };
+//     am::winversion_info ver = {0};
 
-// 	am::system_version::get_win(&ver);
+//     am::system_version::get_win(&ver);
 
-// 	bool is_win8_or_above = am::system_version::is_win8_or_above();
+//     bool is_win8_or_above = am::system_version::is_win8_or_above();
 
-// 	bool is_ia32 = am::system_version::is_32();
+//     bool is_ia32 = am::system_version::is_32();
 
-// 	al_info("win version: %d.%d.%d.%d", ver.major, ver.minor, ver.build, ver.revis);
-// 	al_info("is win8 or above: %s", is_win8_or_above ? "true" : "false");
+//     al_info("win version: %d.%d.%d.%d", ver.major, ver.minor, ver.build, ver.revis);
+//     al_info("is win8 or above: %s", is_win8_or_above ? "true" : "false");
 
-// 	//auto hw_encoders = am::hardware_acceleration::get_supported_video_encoders();
+//     //auto hw_encoders = am::hardware_acceleration::get_supported_video_encoders();
 
-// 	//show_devices();
+//     //show_devices();
 
-// 	//test_audio();
+//     //test_audio();
 
-// 	test_recorder();
+//     test_recorder();
 
-// 	//test_remux();
+//     //test_remux();
 
-// 	//save_aac();
+//     //save_aac();
 
-// 	//test_scale();
+//     //test_scale();
 
-// 	al_info("press any key to exit...");
-// 	system("pause");
+//     al_info("press any key to exit...");
+//     system("pause");
 
-// 	return 0;
+//     return 0;
 // }

+ 3 - 3
widgets/recorderwidget.cpp

@@ -44,10 +44,10 @@ RecorderWidget::RecorderWidget(QWidget *parent)
 {
     // 设置静态实例指针
     s_instance = this;
-    
+    // rtmp://106.55.186.74:1935/stream/V1/0198da41-cdb6-78e3-879d-2ea32d58f73f
     // 初始化默认设置
     m_settings.liveUrl = "rtmp://106.55.186.74:1935/stream/V1";
-    m_settings.liveName = "stream";
+    m_settings.liveName = "0198da41-cdb6-78e3-879d-2ea32d58f73f";
     m_settings.outputDir = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation).toStdString();
     m_settings.videoBitRate = 8000000;
     m_settings.videoFrameRate = 30;
@@ -645,4 +645,4 @@ void RecorderWidget::onPreviewYUVCallback(const unsigned char *data, unsigned in
 void RecorderWidget::onPreviewAudioCallback()
 {
     // 音频预览回调,暂时不处理
-}
+}