/// <summary> /// 开启线程 /// </summary> /// <param name="fileName"></param> /// <param name="sdlVideo"></param> public void Start(string fileName, SDLHelper sdlvideo, SDLAudio sdlAudio) { // 视频线程 threadVideo = new Thread(() => { try { RunVideo(fileName, sdlvideo); } catch (Exception ex) { Console.WriteLine("JT1078CodecForMp4.Run Video", ex); } }); threadVideo.IsBackground = true; threadVideo.Start(); // 音频线程 //threadAudio = new Thread(() => //{ // try // { // RunAudio(fileName, sdlAudio); // } // catch (Exception ex) // { // Console.WriteLine("JT1078CodecForMp4.Run Audio", ex); // } //}); //threadAudio.IsBackground = true; //threadAudio.Start(); }
/// <summary> /// 音频AAC转PCM并使用SDL进行播放 /// </summary> /// <param name="fileName"></param> /// <param name="sdlAudio"></param> /// <returns></returns> public unsafe int RunAudio(string fileName, SDLAudio sdlAudio) { IsRun = true; exit_thread = false; pause_thread = false; threadAudio = Thread.CurrentThread; int error, frame_count = 0; int got_frame, ret; AVFormatContext *ofmt_ctx = null; SwsContext * pSwsCtx = null; IntPtr convertedFrameBufferPtr = IntPtr.Zero; try { // 注册编解码器 ffmpeg.avcodec_register_all(); // 获取文件信息上下文初始化 ofmt_ctx = ffmpeg.avformat_alloc_context(); // 打开媒体文件 error = ffmpeg.avformat_open_input(&ofmt_ctx, fileName, null, null); if (error != 0) { throw new ApplicationException($"ffmepg avformat_open_input error: {error}"); } // 获取流的通道 for (int i = 0; i < ofmt_ctx->nb_streams; i++) { if (ofmt_ctx->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO) { audioindex = i; Console.WriteLine("audio.............." + audioindex); } } if (audioindex == -1) { Console.WriteLine("Couldn't find a audio stream.(没有找到音频流)"); return(-1); } // 音频流处理 if (audioindex > -1) { //根据索引拿到对应的流,根据流拿到解码器上下文 AVCodecContext *pCodeCtx = ofmt_ctx->streams[audioindex]->codec; //再根据上下文拿到编解码id,通过该id拿到解码器 AVCodec *pCodec = ffmpeg.avcodec_find_decoder(pCodeCtx->codec_id); if (pCodec == null) { Console.WriteLine("没有找到编码器"); return(-1); } //打开编码器 if (ffmpeg.avcodec_open2(pCodeCtx, pCodec, null) < 0) { Console.WriteLine("编码器无法打开"); return(-1); } Console.WriteLine("Find a audio stream. channel=" + audioindex); //编码数据 AVPacket *packet = (AVPacket *)ffmpeg.av_malloc((ulong)(sizeof(AVPacket))); //解压缩数据 AVFrame *frame = ffmpeg.av_frame_alloc(); //frame->16bit 44100 PCM 统一音频采样格式与采样率 SwrContext *swrCtx = ffmpeg.swr_alloc(); //重采样设置选项-----------------------------------------------------------start //输入的采样格式 AVSampleFormat in_sample_fmt = pCodeCtx->sample_fmt; //输出的采样格式 16bit PCM AVSampleFormat out_sample_fmt = AVSampleFormat.AV_SAMPLE_FMT_S16; //输入的采样率 int in_sample_rate = pCodeCtx->sample_rate; //输出的采样率 int out_sample_rate = 44100; //输入的声道布局 long in_ch_layout = (long)pCodeCtx->channel_layout; //输出的声道布局 int out_ch_layout = ffmpeg.AV_CH_LAYOUT_MONO; ffmpeg.swr_alloc_set_opts(swrCtx, out_ch_layout, out_sample_fmt, out_sample_rate, in_ch_layout, in_sample_fmt, in_sample_rate, 0, null); ffmpeg.swr_init(swrCtx); //重采样设置选项-----------------------------------------------------------end //获取输出的声道个数 int out_channel_nb = ffmpeg.av_get_channel_layout_nb_channels((ulong)out_ch_layout); //存储pcm数据 byte *out_buffer = (byte *)ffmpeg.av_malloc(2 * 44100); //一帧一帧读取压缩的音频数据AVPacket while (ffmpeg.av_read_frame(ofmt_ctx, packet) >= 0) { // 退出线程 if (exit_thread) { break; } // 暂停解析 if (pause_thread) { while (pause_thread) { Thread.Sleep(100); } } if (packet->stream_index == audioindex) { //解码AVPacket->AVFrame ret = ffmpeg.avcodec_decode_audio4(pCodeCtx, frame, &got_frame, packet); if (ret < 0) { Console.WriteLine("音频解码失败"); return(-1); } // 读取帧数据 if (got_frame > 0) { frame_count++; Console.WriteLine("音频帧数:第 " + frame_count + " 帧"); var data_ = frame->data; ffmpeg.swr_convert(swrCtx, &out_buffer, 2 * 44100, (byte **)&data_, frame->nb_samples); //获取sample的size int out_buffer_size = ffmpeg.av_samples_get_buffer_size(null, out_channel_nb, frame->nb_samples, out_sample_fmt, 1); //写入文件进行测试 var data = out_buffer; sdlAudio.PlayAudio((IntPtr)data, out_buffer_size); } } ffmpeg.av_free_packet(packet); } } } catch (Exception ex) { Console.WriteLine(ex); } finally { if (&ofmt_ctx != null) { ffmpeg.avformat_close_input(&ofmt_ctx);//关闭流文件 } } IsRun = false; return(0); }