陪玩系統原始碼實現音訊編碼的相關步驟
音訊編碼
陪玩系統原始碼中主要的交流方式就是語音連麥,所以音訊編碼在陪玩系統原始碼的開發中是比較重要的一個技術要點,那麼接下來我們一起了解一下音訊編碼是如何實現的吧。
陪玩系統原始碼的編碼與解碼是一對逆過程。下面程式碼是陪玩系統原始碼中音訊解碼的關鍵部分:
av_read_frame(ctx, packet); avcodec_send_packet(ctx, packet); avcodec_receive_frame(ctx, frame);
1、av_read_frame 從檔案中讀取一個 packet
2、avcodec_send_packet 將 packet 送去解碼
3、avcodec_receive_frame 取回解碼好的陪玩系統原始碼資料
編碼的過程剛好是解碼的逆過程,下面程式碼是編碼的關鍵部分:
fill_data_to_frame(frame); avcodec_send_frame(ctx, frame); avcodec_receive_packet(ctx, packet);
1、fill_data_to_frame 將資料填充至 frame 中
2、avcodec_send_frame 將 frame 送去編碼
3、avcodec_receive_packet 取回編碼好的陪玩系統原始碼資料
Show me the code
將陪玩系統原始碼音訊資料編碼為任意格式(只要 ffmpeg 支援)。
// // Created by William.Hua on 2020/9/30. // #if defined(__cplusplus) extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavutil/channel_layout.h> #include <libavutil/common.h> #include <libavutil/frame.h> #include <libavutil/samplefmt.h> #include <libavformat/avformat.h> #if defined(__cplusplus) } #endif #include <iostream> #include <string> using namespace std; static void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, AVFormatContext* pFormatCtx, AVStream* audio_st) { int ret; /* send the frame for encoding */ ret = avcodec_send_frame(ctx, frame); if (ret < 0) { fprintf(stderr, "Error sending the frame to the encoder\n"); exit(1); } /* read all the available output packets (in general there may be any * number of them */ while (ret >= 0) { ret = avcodec_receive_packet(ctx, pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return; else if (ret < 0) { fprintf(stderr, "Error encoding audio frame\n"); exit(1); } pkt->stream_index = audio_st->index; if(frame){ pkt->pts = frame->pts; frame->pts += 100; } av_write_frame(pFormatCtx, pkt); av_packet_unref(pkt); } } int main(int argc, char* argv[]) { if(argc < 2){ cerr << "Usage: encode_audio /full/path/to/output_file\n"; return -1; } const string output_file = argv[1]; // open context AVFormatContext* format_ctx = NULL; avformat_alloc_output_context2(&format_ctx, NULL, NULL, output_file.c_str()); if(!format_ctx){ cerr << "Cannot alloc output context\n"; return -1; } // open output file if(avio_open(&format_ctx->pb, output_file.c_str(), AVIO_FLAG_WRITE) < 0){ cerr << "Cannot open output file\n"; return -1; } // create new audio stream AVStream* audio_st = avformat_new_stream(format_ctx, 0); if(!audio_st){ cerr << "Cannot create audio stream\n"; return -1; } // set codec context parameters const int sample_rate = 44100; const int num_channels = 2; const int bit_rate = 64000; AVCodecContext* codec_ctx = audio_st->codec; codec_ctx->codec_id = format_ctx->oformat->audio_codec; codec_ctx->codec_type = AVMEDIA_TYPE_AUDIO; codec_ctx->sample_rate = sample_rate; codec_ctx->channels = num_channels; codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; // planar float codec_ctx->channel_layout = av_get_default_channel_layout(num_channels); codec_ctx->bit_rate = bit_rate; // print detailed information about output format av_dump_format(format_ctx, 0, output_file.c_str(), 1); // find encode codec AVCodec* codec = avcodec_find_encoder(codec_ctx->codec_id); if(!codec){ cerr << "Cannot find encode codec\n"; return -1; } // open encode codec if(avcodec_open2(codec_ctx, codec, NULL) < 0){ cerr << "Cannot open codec\n"; return -1; } // alloc frame AVFrame* frame = av_frame_alloc(); if(!frame){ cerr << "Cannot alloc frame\n"; return -1; } frame->nb_samples = codec_ctx->frame_size != 0 ? codec_ctx->frame_size : 1024; frame->format = codec_ctx->sample_fmt; frame->channel_layout = codec_ctx->channel_layout; // allocate buffer for frame if(av_frame_get_buffer(frame, 0) < 0){ cerr << "Cannot allocate buffer for frame\n"; return -1; } // make sure frame is writeable if(av_frame_make_writable(frame) < 0){ cerr << "Cannot make frame writeable\n"; return -1; } // alloc packet AVPacket* pkt = av_packet_alloc(); if(!pkt){ cerr << "Cannot allocate packet\n"; return -1; } // write header if(avformat_write_header(format_ctx, NULL) < 0){ cerr << "Cannot write header\n"; return -1; } // write some const int num_output_frame = 500; float t = 0; float tincr = 2 * M_PI * 440.0f / sample_rate; // 440Hz sine wave auto* left_channel = reinterpret_cast<float*>(frame->data[0]); auto* right_channel = reinterpret_cast<float*>(frame->data[1]); frame->pts = 0; for(int i = 0; i < num_output_frame; ++i){ // generate sine wave for(int j = 0; j < frame->nb_samples; ++j){ left_channel[j] = sin(t); right_channel[j] = left_channel[j]; t += tincr; } // encode sine wave, and send them to output file encode(codec_ctx, frame, pkt, format_ctx, audio_st); } // flush encode(codec_ctx, NULL, pkt, format_ctx, audio_st); av_packet_free(&pkt); av_frame_free(&frame); avcodec_close(audio_st->codec); avio_close(format_ctx->pb); avformat_free_context(format_ctx); }
接下來對上述程式碼進行一些解釋與說明
首先,申請並開啟容器。avformat_alloc_output_context2 通過檔名字尾來猜測容器型別。
AVFormatContext* format_ctx = NULL; avformat_alloc_output_context2(&format_ctx, NULL, NULL, output_file.c_str());
接著開啟陪玩系統原始碼輸出檔案。
avio_open(&format_ctx->pb, output_file.c_str(), AVIO_FLAG_WRITE)
在容器中建立一條音訊流,用於輸出編碼後的陪玩系統原始碼資料。
AVStream* audio_st = avformat_new_stream(format_ctx, 0);
設定編碼器的各類引數,包括取樣率、通道數、位元率等。需要注意的是,為了簡化程式碼,這裡採用 AV_SAMPLE_FMT_FLTP 作為取樣格式,但是有些格式(例如 .flac )是不支援 AV_SAMPLE_FMT_FLTP 的,在實際專案中,應該判斷當前容器型別是否支援取樣格式,判斷的程式碼大家可以在 encode_audio.c 找到靈感,這裡不再展開說了。
const int sample_rate = 44100; const int num_channels = 2; const int bit_rate = 64000; AVCodecContext* codec_ctx = audio_st->codec; codec_ctx->codec_id = format_ctx->oformat->audio_codec; codec_ctx->codec_type = AVMEDIA_TYPE_AUDIO; codec_ctx->sample_rate = sample_rate; codec_ctx->channels = num_channels; codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; // planar float codec_ctx->channel_layout = av_get_default_channel_layout(num_channels); codec_ctx->bit_rate = bit_rate;
找到並開啟陪玩系統原始碼的編碼器。
AVCodec* codec = avcodec_find_encoder(codec_ctx->codec_id); avcodec_open2(codec_ctx, codec, NULL);
申請 AVFrame 用於存放資料。av_frame_get_buffer 通過 nb_samples, sample_fmt, channel_layout 資訊計算需要的記憶體大小,並進行申請。av_frame_make_writable 保證 AVFrame 是可寫的。
AVFrame* frame = av_frame_alloc(); frame->nb_samples = codec_ctx->frame_size != 0 ? codec_ctx->frame_size : 1024; frame->format = codec_ctx->sample_fmt; frame->channel_layout = codec_ctx->channel_layout; av_frame_get_buffer(frame, 0); av_frame_make_writable(frame);
寫檔案頭。
avformat_write_header(format_ctx, NULL);
生成正弦波,並寫入檔案。由於取樣格式為 AV_SAMPLE_FMT_FLTP,且聲道數為 2,因此 frame->data[0] 為左聲道,frame->data[1] 為右聲道。
auto* left_channel = reinterpret_cast<float*>(frame->data[0]); auto* right_channel = reinterpret_cast<float*>(frame->data[1]); for(int j = 0; j < frame->nb_samples; ++j){ left_channel[j] = sin(t); right_channel[j] = left_channel[j]; t += tincr; } encode(codec_ctx, frame, pkt, format_ctx, audio_st);
在 encode 函式中,將 frame 送去編碼,接著通過 avcodec_receive_packet 取出編碼後的 packet,最後將 packet 寫入檔案。
avcodec_send_frame(ctx, frame); while (ret >= 0) { ret = avcodec_receive_packet(ctx, pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return; else if (ret < 0) { fprintf(stderr, "Error encoding audio frame\n"); exit(1); } pkt->stream_index = audio_st->index; if(frame){ pkt->pts = frame->pts; frame->pts += 100; } av_write_frame(pFormatCtx, pkt); av_packet_unref(pkt); }
進行 flush,把剩餘資料一網打盡。
encode(codec_ctx, NULL, pkt, format_ctx, audio_st);
最後別忘記釋放記憶體。
av_packet_free(&pkt); av_frame_free(&frame); avcodec_close(audio_st->codec); avio_close(format_ctx->pb); avformat_free_context(format_ctx);
總結
我們介紹瞭如何在陪玩系統原始碼中實現音訊編碼。編碼與解碼是一對逆過程,理解這一概念後,我們給出了編碼的具體程式碼,並對其進行了說明與解釋,學會了如何寫檔案頭,如何申請音訊幀記憶體,以及如何編碼資料等。
本文轉載自網路,轉載僅為分享乾貨知識,如有侵權歡迎聯絡雲豹科技進行刪除處理
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69996194/viewspace-2842482/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 如何開發陪玩系統原始碼的列表頁面,相關實現程式碼原始碼
- 遊戲陪玩系統開發,音視訊混流的實現程式碼遊戲
- 陪玩原始碼,與時間、日期相關的程式碼分析原始碼
- 如何實現遊戲陪玩系統原始碼前端效能監控?遊戲原始碼前端
- 陪玩系統原始碼中mysql資料庫備份還原的實現程式碼原始碼MySql資料庫
- 陪玩系統原始碼中陣列去重的實現程式碼,簡單卻重要原始碼陣列
- 如何在遊戲陪玩系統原始碼中實現“刮刮樂”效果?遊戲原始碼
- 相親交友原始碼中,音訊AAC解碼的實現程式碼原始碼音訊
- 遊戲陪玩系統原始碼中不同排序演算法的實現方式遊戲原始碼排序演算法
- 遊戲陪玩原始碼的登入方式,簡訊驗證碼登入的實現遊戲原始碼
- 關於遊戲陪玩系統原始碼後臺管理系統,需要思考的二三事遊戲原始碼
- 做好陪玩系統原始碼的前端效能優化,提升系統效能原始碼前端優化
- 在相親交友原始碼中實現視訊連麥直播需要哪些步驟?原始碼
- 遊戲陪玩系統原始碼中懶載入的實現方式有哪幾種?遊戲原始碼
- 陪玩系統原始碼開發,H5頁面中呼叫支付功能的實現原始碼H5
- 如何在遊戲陪玩app原始碼中實現簡訊驗證碼登入?遊戲APP原始碼
- 語音陪玩原始碼如何做到不卡頓?原始碼
- 遊戲陪玩系統原始碼開發,如何實現圖片和動畫的優化?遊戲原始碼動畫優化
- 如何在遊戲陪玩系統原始碼中聊天室內實現一個禮物系統?遊戲原始碼
- 從比心APP原始碼的成功,分析陪玩系統原始碼應該如何開發APP原始碼
- Android短影片系統硬編碼—實現音影片編碼(三)Android
- Android短影片系統硬編碼—實現音影片編碼(二)Android
- 遊戲陪玩系統原始碼的許可權設計,如何基於位運算實現?遊戲原始碼
- 如何進行遊戲陪玩系統原始碼中音視訊的自動化測試?遊戲原始碼
- 用 Go + Redis 實現陪玩平臺原始碼中的分散式鎖GoRedis原始碼分散式
- 如何實現遊戲陪玩系統中語音的錄製與播放?遊戲
- 如何使用 Redis 實現 陪玩原始碼中“附近的人” 這一功能?Redis原始碼
- 遊戲陪玩原始碼的移動端適配,應該如何實現?遊戲原始碼
- 遊戲陪玩app原始碼的可靠訊息最終一致性方案的實現遊戲APP原始碼
- 陪玩系統原始碼利用介面非同步呼叫,減少介面耗時原始碼非同步
- 陪玩平臺原始碼中的排序演算法,插入排序的實現原始碼排序演算法
- 帶你瞭解遊戲陪玩系統原始碼前端常用的儲存方式遊戲原始碼前端
- 語音直播系統原始碼與視訊直播系統原始碼哪些區別原始碼
- 在相親原始碼的多人音視訊聊天中插入現場直播的實現方式原始碼
- Win10系統如何關閉音訊服務_Win10關閉音訊服務的步驟Win10音訊
- 陪玩系統原始碼移動前端開發需要注意的20個要點原始碼前端
- 陪玩系統原始碼開發,不懂資料庫隔離級別的請進原始碼資料庫
- 遊戲陪玩app原始碼開發,常用的倒數計時功能如何實現?遊戲APP原始碼