FFmpeg淺嘗輒止(四)——音訊的解碼和編碼
阿新 • • 發佈:2019-02-05
音訊和視訊其實是一樣的,在檔案中尋找音訊流,然後解壓出來,得到音訊幀的資料,同樣也可以按照設定的編碼格式進行壓縮,我這裡把音訊的解碼和編碼做成了兩個工程,也是直接上程式碼:
#include <stdio.h> #include <stdlib.h> extern "C" { #include <libavcodec\avcodec.h> #include <libavformat\avformat.h> #include <libswscale\swscale.h> } int main(char arg,char *argv[]) { char *filename = argv[1]; av_register_all(); //註冊所有可解碼型別 AVFormatContext *pInFmtCtx=NULL; //檔案格式 AVCodecContext *pInCodecCtx=NULL; //編碼格式 if (av_open_input_file(&pInFmtCtx, filename, NULL, 0, NULL)!=0) //獲取檔案格式 printf("av_open_input_file error\n"); if (av_find_stream_info(pInFmtCtx) < 0) //獲取檔案內音視訊流的資訊 printf("av_find_stream_info error\n"); unsigned int j; // Find the first audio stream int audioStream = -1; for (j=0; j<pInFmtCtx->nb_streams; j++) //找到音訊對應的stream { if (pInFmtCtx->streams[j]->codec->codec_type == CODEC_TYPE_AUDIO) { audioStream = j; break; } } if (audioStream == -1) { printf("input file has no audio stream\n"); return 0; // Didn't find a audio stream } printf("audio stream num: %d\n",audioStream); pInCodecCtx = pInFmtCtx->streams[audioStream]->codec; //音訊的編碼上下文 AVCodec *pInCodec = NULL; pInCodec = avcodec_find_decoder(pInCodecCtx->codec_id); //根據編碼ID找到用於解碼的結構體 if (pInCodec == NULL) { printf("error no Codec found\n"); return -1 ; // Codec not found } if(avcodec_open(pInCodecCtx, pInCodec)<0)//將兩者結合以便在下面的解碼函式中呼叫pInCodec中的對應解碼函式 { printf("error avcodec_open failed.\n"); return -1; // Could not open codec } static AVPacket packet; printf(" bit_rate = %d \r\n", pInCodecCtx->bit_rate); printf(" sample_rate = %d \r\n", pInCodecCtx->sample_rate); printf(" channels = %d \r\n", pInCodecCtx->channels); printf(" code_name = %s \r\n", pInCodecCtx->codec->name); printf(" block_align = %d\n",pInCodecCtx->block_align); uint8_t *pktdata; int pktsize; int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100; uint8_t * inbuf = (uint8_t *)malloc(out_size); FILE* pcm; pcm = fopen("result.pcm","wb"); long start = clock(); while (av_read_frame(pInFmtCtx, &packet) >= 0)//pInFmtCtx中呼叫對應格式的packet獲取函式 { if(packet.stream_index==audioStream)//如果是音訊 { pktdata = packet.data; pktsize = packet.size; while(pktsize>0) { out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100; //解碼 int len = avcodec_decode_audio2(pInCodecCtx, (short*)inbuf, &out_size, pktdata, pktsize); if (len < 0) { printf("Error while decoding.\n"); break; } if(out_size > 0) { fwrite(inbuf,1,out_size,pcm);//pcm記錄 fflush(pcm); } pktsize -= len; pktdata += len; } } av_free_packet(&packet); } long end = clock(); printf("cost time :%f\n",double(end-start)/(double)CLOCKS_PER_SEC); free(inbuf); fclose(pcm); if (pInCodecCtx!=NULL) { avcodec_close(pInCodecCtx); } av_close_input_file(pInFmtCtx); return 0; }
解碼後的檔案儲存為result.pcm中,現在用這個檔案壓縮出新的音訊檔案,程式碼如下:
void main() { int16_t *samples; uint8_t *audio_outbuf; int audio_outbuf_size; int audio_input_frame_size; double audio_pts; const char* filename = "test.wav"; FILE *fin = fopen("result.pcm", "rb"); //音訊原始檔 AVOutputFormat *fmt; AVFormatContext *oc; AVStream * audio_st; av_register_all(); fmt = guess_format(NULL, filename, NULL); oc = av_alloc_format_context(); oc->oformat = fmt; snprintf(oc->filename, sizeof(oc->filename), "%s", filename); audio_st = NULL; if (fmt->audio_codec != CODEC_ID_NONE) { AVCodecContext *c; audio_st = av_new_stream(oc, 1); c = audio_st->codec; c->codec_id = fmt->audio_codec; c->codec_type = CODEC_TYPE_AUDIO; c->bit_rate = 128000; c->sample_rate = 44100; c->channels = 2; } if (av_set_parameters(oc, NULL) < 0) { return; } dump_format(oc, 0, filename, 1); if (audio_st) { AVCodecContext* c; AVCodec* codec; c = audio_st->codec; codec = avcodec_find_encoder(c->codec_id); avcodec_open(c, codec); audio_outbuf_size = 10000; audio_outbuf = (uint8_t*)av_malloc(audio_outbuf_size); if (c->frame_size <= 1) { audio_input_frame_size = audio_outbuf_size / c->channels; switch (audio_st->codec->codec_id) { case CODEC_ID_PCM_S16LE: case CODEC_ID_PCM_S16BE: case CODEC_ID_PCM_U16LE: case CODEC_ID_PCM_U16BE: audio_input_frame_size >>= 1; break; default: break; } } else { audio_input_frame_size = c->frame_size; } samples = (int16_t*)av_malloc(audio_input_frame_size*2*c->channels); } if (!fmt->flags & AVFMT_NOFILE) { if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) { return; } } av_write_header(oc); for (;;) { if (audio_st) { audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den; } else { audio_pts = 0.0; } if (!audio_st || audio_pts >= 360.0) { break; } if (fread(samples, 1, audio_input_frame_size*2*audio_st->codec->channels, fin) <= 0) { break; } AVCodecContext* c; AVPacket pkt; av_init_packet(&pkt); c = audio_st->codec; pkt.size = avcodec_encode_audio(c, audio_outbuf, audio_outbuf_size, samples); pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, audio_st->time_base); pkt.flags |= PKT_FLAG_KEY; pkt.stream_index = audio_st->index; pkt.data = audio_outbuf; if (av_write_frame(oc, &pkt) != 0) { return; } } if (audio_st) { avcodec_close(audio_st->codec); av_free(samples); av_free(audio_outbuf); } av_write_trailer(oc); for (int i=0; i<oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } if (!(fmt->flags & AVFMT_NOFILE)) { url_fclose(oc->pb); } av_free(oc); fclose(fin); }
對應的下載連結:
至此,我已經實現了視訊的解碼編碼,音訊的解碼編碼,相信有心人肯定可以在此基礎上實現視訊音訊的同步壓縮,不過要再看一些網上的資料,也是很輕鬆的。至於視訊的顯示播放,大多數是要結合SDL實現,由於我只是淺嘗輒止,只實現了圖片的顯示和簡單的視訊播放,比起我之前推薦的幾個連結是弱爆了的,就不寫下去了,這個系列就到這裡吧,希望能給像我一樣的一些入門級選手點幫助。也歡迎大神來多多指教。