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

Windwos平台上ffmpeg解码音频并且保存到wav文件中

 

你可能感兴趣的:(ffmpeg)