Windwos平台上ffmpeg解码音频并且保存到wav文件中
先附上代码,测试通过
#include <stdio.h> #include <math.h> #include "libavutil/avstring.h" //修改colorspace.h中的inline为__inline #include "libavutil/colorspace.h" #include "libavutil/pixdesc.h" #include "libavutil/imgutils.h" #include "libavutil/dict.h" #include "libavutil/parseutils.h" #include "libavutil/samplefmt.h" #include "libavutil/avassert.h" #include "libavformat/avformat.h" #include "libavdevice/avdevice.h" #include "libswscale/swscale.h" #include "libavcodec/audioconvert.h" #include "libavutil/opt.h" #include "libavcodec/avfft.h" #include "cmdutils.h" #include "pthread.h" static AVPacket flush_pkt;//暂时不知道flush_pkt有什么作用,暂时先放这里。 //#define DEBUG_SYNC #define MAX_QUEUE_SIZE (15 * 1024 * 1024) #define MIN_AUDIOQ_SIZE (20 * 16 * 1024) #define MIN_FRAMES 5 /* SDL audio buffer size, in samples. Should be small to have precise A/V sync as SDL does not have hardware buffer fullness info. */ #define SDL_AUDIO_BUFFER_SIZE 1024 /* no AV sync correction is done if below the AV sync threshold */ #define AV_SYNC_THRESHOLD 0.01 /* no AV correction is done if too big error */ #define AV_NOSYNC_THRESHOLD 10.0 #define FRAME_SKIP_FACTOR 0.05 /* maximum audio speed change to get correct sync */ #define SAMPLE_CORRECTION_PERCENT_MAX 10 /* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */ #define AUDIO_DIFF_AVG_NB 20 /* NOTE: the size must be big enough to compensate the hardware audio buffersize size */ #define SAMPLE_ARRAY_SIZE (2*65536) typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; int nb_packets; int size; int abort_request; pthread_mutex_t *mutex;//互斥锁 pthread_cond_t *cond;//条件变量 } PacketQueue; #define VIDEO_PICTURE_QUEUE_SIZE 2 #define SUBPICTURE_QUEUE_SIZE 4 typedef struct VideoPicture { double pts; ///<presentation time stamp for this picture double target_clock; ///<av_gettime() time at which this should be displayed ideally int64_t pos; ///<byte position in file // SDL_Overlay *bmp; int width, height; /* source height & width */ int allocated; enum PixelFormat pix_fmt; #if CONFIG_AVFILTER AVFilterBufferRef *picref; #endif } VideoPicture; typedef struct SubPicture { double pts; /* presentation time stamp for this picture */ AVSubtitle sub; } SubPicture; enum { AV_SYNC_AUDIO_MASTER, /* default choice */ AV_SYNC_VIDEO_MASTER, AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */ }; typedef struct VideoState { pthread_t *parse_tid; //SDL_Thread *parse_tid; pthread_t *video_tid; //SDL_Thread *video_tid; pthread_t *refresh_tid; //SDL_Thread *refresh_tid; AVInputFormat *iformat; int no_background; int abort_request; int paused; int last_paused; int seek_req; int seek_flags; int64_t seek_pos; int64_t seek_rel; int read_pause_return; AVFormatContext *ic; int dtg_active_format; int audio_stream; int av_sync_type; double external_clock; /* external clock base */ int64_t external_clock_time; double audio_clock; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; int audio_diff_avg_count; AVStream *audio_st; PacketQueue audioq; int audio_hw_buf_size; /* samples output by the codec. we reserve more space for avsync compensation */ DECLARE_ALIGNED(16,uint8_t,audio_buf1)[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; DECLARE_ALIGNED(16,uint8_t,audio_buf2)[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; uint8_t *audio_buf; unsigned int audio_buf_size; /* in bytes */ int audio_buf_index; /* in bytes */ AVPacket audio_pkt_temp; AVPacket audio_pkt; enum AVSampleFormat audio_src_fmt; AVAudioConvert *reformat_ctx; enum ShowMode { SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, SHOW_MODE_WAVES, SHOW_MODE_RDFT, SHOW_MODE_NB } show_mode; int16_t sample_array[SAMPLE_ARRAY_SIZE]; int sample_array_index; int last_i_start; RDFTContext *rdft; int rdft_bits; FFTSample *rdft_data; int xpos; pthread_t *subtitle_tid; //SDL_Thread *subtitle_tid; int subtitle_stream; int subtitle_stream_changed; AVStream *subtitle_st; PacketQueue subtitleq; SubPicture subpq[SUBPICTURE_QUEUE_SIZE]; int subpq_size, subpq_rindex, subpq_windex; pthread_mutex_t *subpq_mutex; pthread_cond_t *subpq_cond; //SDL_mutex *subpq_mutex; //SDL_cond *subpq_cond; double frame_timer; double frame_last_pts; double frame_last_delay; double video_clock; ///<pts of last decoded frame / predicted pts of next decoded frame int video_stream; AVStream *video_st; PacketQueue videoq; double video_current_pts; ///<current displayed pts (different from video_clock if frame fifos are used) double video_current_pts_drift; ///<video_current_pts - time (av_gettime) at which we updated video_current_pts - used to have running video pts int64_t video_current_pos; ///<current displayed file pos VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; int pictq_size, pictq_rindex, pictq_windex; pthread_mutex_t *pictq_mutex; //SDL_mutex *pictq_mutex; pthread_cond_t *pictq_cond; //SDL_cond *pictq_cond; struct SwsContext *img_convert_ctx; // QETimer *video_timer; char filename[1024]; int width, height, xleft, ytop; //PtsCorrectionContext pts_ctx; float skip_frames; float skip_frames_index; int refresh; } VideoState; static int opt_help(const char *opt, const char *arg); /* options specified by the user */ static AVInputFormat *file_iformat; static const char *input_filename; static const char *window_title; static int fs_screen_width; static int fs_screen_height; static int screen_width = 0; static int screen_height = 0; static int frame_width = 0; static int frame_height = 0; static enum PixelFormat frame_pix_fmt = PIX_FMT_NONE; static int audio_disable; static int video_disable; /* static int wanted_stream[AVMEDIA_TYPE_NB]={ [AVMEDIA_TYPE_AUDIO]=-1, [AVMEDIA_TYPE_VIDEO]=-1, [AVMEDIA_TYPE_SUBTITLE]=-1, }; */ static int wanted_stream[AVMEDIA_TYPE_NB]={-1,-1,0,-1,0}; static int seek_by_bytes=-1; static int display_disable; static int show_status = 1; static int av_sync_type = AV_SYNC_AUDIO_MASTER; static int64_t start_time = AV_NOPTS_VALUE; static int64_t duration = AV_NOPTS_VALUE; static int step = 0; static int thread_count = 1; static int workaround_bugs = 1; static int fast = 0; static int genpts = 0; static int lowres = 0; static int idct = FF_IDCT_AUTO; static enum AVDiscard skip_frame= AVDISCARD_DEFAULT; static enum AVDiscard skip_idct= AVDISCARD_DEFAULT; static enum AVDiscard skip_loop_filter= AVDISCARD_DEFAULT; static int error_recognition = FF_ER_CAREFUL; static int error_concealment = 3; static int decoder_reorder_pts= -1; static int autoexit; static int exit_on_keydown; static int exit_on_mousedown; static int loop=1; static int framedrop=-1; static enum ShowMode show_mode = SHOW_MODE_NONE; static int rdftspeed=20; #if CONFIG_AVFILTER static char *vfilters = NULL; #endif /* current context */ static int is_full_screen; static VideoState *cur_stream; static int64_t audio_callback_time; static AVPacket flush_pkt;//暂时不知道flush_pkt有什么作用,暂时先放这里。 static int packet_queue_put(PacketQueue *q, AVPacket *pkt); /* packet queue handling */ //初始化队列 static void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); pthread_mutex_init(q->mutex,NULL); pthread_cond_init(q->cond,NULL); //q->mutex = SDL_CreateMutex(); //q->cond = SDL_CreateCond(); packet_queue_put(q, &flush_pkt); } //清空队列 static void packet_queue_flush(PacketQueue *q) { AVPacketList *pkt, *pkt1; pthread_mutex_lock(q->mutex); //SDL_LockMutex(q->mutex); for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { pkt1 = pkt->next; av_free_packet(&pkt->pkt); av_freep(&pkt); } q->last_pkt = NULL; q->first_pkt = NULL; q->nb_packets = 0; q->size = 0; pthread_mutex_unlock(q->mutex); //SDL_UnlockMutex(q->mutex); } static void packet_queue_end(PacketQueue *q) { packet_queue_flush(q); pthread_mutex_destroy(q->mutex); pthread_cond_destroy(q->cond); //SDL_DestroyMutex(q->mutex); //SDL_DestroyCond(q->cond); } static int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; /* duplicate the packet */ if (pkt!=&flush_pkt && av_dup_packet(pkt) < 0) return -1; pkt1 = av_malloc(sizeof(AVPacketList)); if (!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL; pthread_mutex_lock(q->mutex); // SDL_LockMutex(q->mutex); if (!q->last_pkt) q->first_pkt = pkt1; else q->last_pkt->next = pkt1; q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size + sizeof(*pkt1); /* XXX: should duplicate packet data in DV case */ pthread_cond_signal(q->cond); // SDL_CondSignal(q->cond); // SDL_UnlockMutex(q->mutex); pthread_mutex_unlock(q->mutex); return 0; } static void packet_queue_abort(PacketQueue *q) { pthread_mutex_lock(q->mutex); //SDL_LockMutex(q->mutex); q->abort_request = 1; pthread_cond_signal(q->cond); //SDL_CondSignal(q->cond); pthread_mutex_unlock(q->mutex); //SDL_UnlockMutex(q->mutex); } //packet_queue_get 函数被调用的地方是audio_decode_frame,subtitle_thread,get_video_frame中, //作用是从队列q中读取block(一般为)个packet,留待下一次进行解码 //avcodec_decode_audio3,avcodec_decode_video2 /* return < 0 if aborted, 0 if no packet and > 0 if packet. */ static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { AVPacketList *pkt1; int ret; pthread_mutex_lock(q->mutex); //SDL_LockMutex(q->mutex); for(;;) { if (q->abort_request) { ret = -1; break; } pkt1 = q->first_pkt; if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) q->last_pkt = NULL; q->nb_packets--; q->size -= pkt1->pkt.size + sizeof(*pkt1); *pkt = pkt1->pkt; av_free(pkt1); ret = 1; break; } else if (!block) { ret = 0; break; } else { pthread_cond_wait(q->cond,q->mutex); //SDL_CondWait(q->cond, q->mutex); } } pthread_mutex_unlock(q->mutex); //SDL_UnlockMutex(q->mutex); return ret; } //声明了一个内联函数,写wav头 static __inline void writeWavHeader(AVCodecContext *pCodecCtx,AVFormatContext *pFormatCtx,FILE *audioFile) { //wav文件有44字节的wav头,所以要写44字节的wav头 int8_t *data; int32_t long_temp; int16_t short_temp; int16_t BlockAlign; int bits=16; int32_t fileSize; int32_t audioDataSize; switch(pCodecCtx->sample_fmt) { case AV_SAMPLE_FMT_S16: bits=16; break; case AV_SAMPLE_FMT_S32: bits=32; break; case AV_SAMPLE_FMT_U8: bits=8; break; default: bits=16; break; } audioDataSize=(pFormatCtx->duration)*(bits/8)*(pCodecCtx->sample_rate)*(pCodecCtx->channels); fileSize=audioDataSize+36; data="RIFF"; fwrite(data,sizeof(char),4,audioFile); fwrite(&fileSize,sizeof(int32_t),1,audioFile); //"WAVE" data="WAVE"; fwrite(data,sizeof(char),4,audioFile); data="fmt "; fwrite(data,sizeof(char),4,audioFile); long_temp=16; fwrite(&long_temp,sizeof(int32_t),1,audioFile); short_temp=0x01; fwrite(&short_temp,sizeof(int16_t),1,audioFile); short_temp=(pCodecCtx->channels); fwrite(&short_temp,sizeof(int16_t),1,audioFile); long_temp=(pCodecCtx->sample_rate); fwrite(&long_temp,sizeof(int32_t),1,audioFile); long_temp=(bits/8)*(pCodecCtx->channels)*(pCodecCtx->sample_rate); fwrite(&long_temp,sizeof(int32_t),1,audioFile); BlockAlign=(bits/8)*(pCodecCtx->channels); fwrite(&BlockAlign,sizeof(int16_t),1,audioFile); short_temp=(bits); fwrite(&short_temp,sizeof(int16_t),1,audioFile); data="data"; fwrite(data,sizeof(char),4,audioFile); fwrite(&audioDataSize,sizeof(int32_t),1,audioFile); fseek(audioFile,44,SEEK_SET); } int main() { // char *filename="rtsp://192.168.20.112/Love_You.mp4"; //char *filename="E:\\flv\\3d.mp3"; char *filename="E:\\flv\\MY.aac"; // char *filename="mms://mms.cnr.cn/cnr003"; // char *filename="mms://mms.cnr.cn/cnr001"; // char *filename="rtsp://livewm.orange.fr/live-multicanaux"; // char *filename="mms://211.167.102.66/ch-01"; AVFormatContext *pFormatCtx; int audioStream=-1; int i; int iFrame=0; AVCodecContext *pCodecCtx; AVCodec *pCodec=NULL; static AVPacket packet; uint8_t *pktData=NULL; int pktSize; int outSize=AVCODEC_MAX_AUDIO_FRAME_SIZE; // FILE *wavfile=NULL; //这里必须使用av_malloc uint8_t *inbuf=(uint8_t *)av_malloc(outSize); FILE *wavFile=NULL; int32_t audioFileSize=0; //注册所有的编解码器 av_register_all(); //打开文件 if(av_open_input_file(&pFormatCtx,filename,NULL,0,NULL)!=0) { printf("Could not open input file %s\n",filename); return 0; } if(av_find_stream_info(pFormatCtx)<0) { printf("Could not find stream information\n"); } //输出文件的音视频流信息 av_dump_format(pFormatCtx,0,filename,0); //找到音频流 for(i=0;i<pFormatCtx->nb_streams;i++) { if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO) { audioStream=i; break; } } //找到解码器 pCodecCtx=pFormatCtx->streams[audioStream]->codec; pCodec=avcodec_find_decoder(pCodecCtx->codec_id); //打开解码器 if(avcodec_open(pCodecCtx,pCodec)<0) { printf("Error avcodec_open failed.\n"); return 1; } printf("\tbit_rate=%d\n \ bytes_per_secondes=%d\n \ sample_rate=%d\n \ channels=%d\n \ codec_name=%s\n",pCodecCtx->bit_rate,(pCodecCtx->codec_id==CODEC_ID_PCM_U8)?8:16, pCodecCtx->sample_rate,pCodecCtx->channels,pCodecCtx->codec->name); //wavFile=fopen("E:\\flv\\saveWav.wav","wb"); wavFile=fopen("E:\\flv\\MY.wav","wb"); //wavFile=fopen("E:\\flv\\test.wav","wb"); if (wavFile==NULL) { printf("open error\n"); return 1; } //写入wav文件头 writeWavHeader(pCodecCtx,pFormatCtx,wavFile); //开始解码音频流 av_free_packet(&packet); while(av_read_frame(pFormatCtx,&packet)>=0) { if(packet.stream_index==audioStream) { int len=0; if((iFrame++)>=4000) break; pktData=packet.data; pktSize=packet.size; while(pktSize>0) { outSize=AVCODEC_MAX_AUDIO_FRAME_SIZE; len=avcodec_decode_audio3(pCodecCtx,(short *)inbuf,&outSize,&packet); if(len<0){ printf("Error while decoding\n"); break; } if(outSize>0) { audioFileSize+=outSize; fwrite(inbuf,1,outSize,wavFile); fflush(wavFile); } pktSize-=len; pktData+=len; } } av_free_packet(&packet); } //wav文件的第40个字节开始的4个字节存放的是wav文件的有效数据长度 fseek(wavFile,40,SEEK_SET); fwrite(&audioFileSize,1,sizeof(int32_t),wavFile); //wav文件的第4个字节开始的4个字节存放的是wav文件的文件长度(audioFileSize+44-8),44表示44个字节的头,8表示"RIFF"和"WAVE" audioFileSize+=36; fseek(wavFile,4,SEEK_SET); fwrite(&audioFileSize,1,sizeof(int32_t),wavFile); //关闭文件 fclose(wavFile); //释放内存 av_free(inbuf); if(pCodecCtx!=NULL){ avcodec_close(pCodecCtx); } av_close_input_file(pFormatCtx); return 0; }
需要用到的音视频解码静态库文件包括以下几个:
avcodec-53.lib,avdevice-53.lib,avfilter-2.lib,avformat-53.lib,avutil-51.lib,pthreadVC2.lib,swscale-2.lib