zoukankan      html  css  js  c++  java
  • SDL播放声音

    extern "C"
    {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
    #include <libswresample/swresample.h>
    #include <libavutil/avstring.h>
    #include <libavutil/pixfmt.h>
    #include <libavutil/log.h>
    };
    
    #include <stdio.h>
    #include <math.h>
    #include <SDL.h>
    #include <SDL_thread.h>
    
    
    #pragma comment(lib, "libmingwex.lib")
    #pragma comment(lib, "libgcc.lib")
    #pragma comment(lib, "avformat.lib")
    #pragma comment(lib, "avutil.lib")
    #pragma comment(lib, "avcodec.lib")
    #pragma comment(lib, "swresample.lib")
    
    #pragma comment(lib, "SDL.lib")
    #pragma comment(lib, "SDLmain.lib")
    
    #define SDL_AUDIO_BUFFER_SIZE 1024 
    #define MAX_AUDIOQ_SIZE (1 * 1024 * 1024)
    #define FF_ALLOC_EVENT   (SDL_USEREVENT)
    #define FF_REFRESH_EVENT (SDL_USEREVENT + 1)
    #define FF_QUIT_EVENT (SDL_USEREVENT + 2)
    
    typedef struct PacketQueue {//Queue
        AVPacketList *first_pkt, *last_pkt;
        int nb_packets;
        int size;
        SDL_mutex *mutex;
        SDL_cond *cond;
    } PacketQueue;
    
    typedef struct VideoState {//State
        char            filename[1024];
        AVFormatContext *ic;
        AVCodecContext *pCodecCtx;
    	AVCodec        *pCodec;
        int             videoStream, audioStream;
        AVStream        *audio_st;
        AVFrame         *audio_frame;
        PacketQueue     audioq;
        unsigned int    audio_buf_size;
        unsigned int    audio_buf_index;
        AVPacket        audio_pkt;
        uint8_t         *audio_pkt_data;
        int             audio_pkt_size;
        uint8_t         *audio_buf;
        uint8_t         *audio_buf1;
        DECLARE_ALIGNED(16,uint8_t,audio_buf2)[AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
        enum AVSampleFormat  audio_src_fmt;
        enum AVSampleFormat  audio_tgt_fmt;
        int             audio_src_channels;
        int             audio_tgt_channels;
        int64_t         audio_src_channel_layout;
        int64_t         audio_tgt_channel_layout;
        int             audio_src_freq;
        int             audio_tgt_freq;
        struct SwrContext *swr_ctx;
        SDL_Thread      *parse_tid;//thread id
        int             quit;//flag 
    } VideoState;
    
    VideoState *global_video_state;//global state
    
    void packet_queue_init(PacketQueue *q) {//init  queue
        memset(q, 0, sizeof(PacketQueue));
        q->mutex = SDL_CreateMutex();
        q->cond = SDL_CreateCond();
    }
    
    int packet_queue_put(PacketQueue *q, AVPacket *pkt) {//put pkt to pktQueue
        AVPacketList *pkt1;
    
        pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
        if (!pkt1) {
            return -1;
        }
        pkt1->pkt = *pkt;//put pkt to PacketList 
        pkt1->next = NULL;
    
        SDL_LockMutex(q->mutex);
    
        if (!q->last_pkt) {//last_pkt = NULL, Queue element is  PacketList,we can not use packet directly
            q->first_pkt = pkt1;
        } else {//not first time
            q->last_pkt->next = pkt1;
        }
    
        q->last_pkt = pkt1;//update
        q->nb_packets++;//record nb_packet
        q->size += pkt1->pkt.size;//update pkt size in queue
        SDL_CondSignal(q->cond);
        SDL_UnlockMutex(q->mutex);
        return 0;
    }
    
    static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {//get data from queue
        AVPacketList *pkt1;
        int ret;
    
        SDL_LockMutex(q->mutex);//lock
    
        for(;;) {
            if(global_video_state->quit) {//is or not quit firstly
                ret = -1;
                break;
            }//quit,跳出循环
    
            pkt1 = q->first_pkt;//queue: first in, first out
            if (pkt1) {       
                q->first_pkt = pkt1->next;
                if (!q->first_pkt) {//if queue is over
                    q->last_pkt = NULL;
                }
                q->nb_packets--;
                q->size -= pkt1->pkt.size;
                *pkt = pkt1->pkt;
    
                av_free(pkt1);
                ret = 1;//
                break;
            } else if (!block) {
                ret = 0;
                break;
            } else {
                SDL_CondWait(q->cond, q->mutex);//until data enough 
            }
        }
        SDL_UnlockMutex(q->mutex);
        return ret;
    }
    
    static void packet_queue_flush(PacketQueue *q) {//flush queue
        AVPacketList *pkt, *pkt1;
    
        SDL_LockMutex(q->mutex);//lock
        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;
        SDL_UnlockMutex(q->mutex);//unlock
    }
    
    int audio_decode_frame(VideoState *is) {//core code   decoded information is saved in videostate-is
        int len1, len2, decoded_data_size;
        AVPacket *pkt = &is->audio_pkt;
        int got_frame = 0;
        int64_t dec_channel_layout;
        int wanted_nb_samples, resampled_data_size;
    
        for (;;) {//dead loop
            while (is->audio_pkt_size > 0) {
                if (!is->audio_frame) {
                    if (!(is->audio_frame = avcodec_alloc_frame())) {
                        return AVERROR(ENOMEM);           
                    }
                } else 
                    avcodec_get_frame_defaults(is->audio_frame);//AVFrame should be set to default values
                len1 = avcodec_decode_audio4(is->audio_st->codec, is->audio_frame, &got_frame,  pkt);
                if (len1 < 0) {
                    // error, skip the frame
                    is->audio_pkt_size = 0;
                    break;
                }
    
                is->audio_pkt_data += len1;
                is->audio_pkt_size -= len1;
    
                if (!got_frame) 
                    continue;
                                  
               /* decoded_data_size = av_samples_get_buffer_size(NULL,
                                    is->audio_frame->channels,							 
                                   is->audio_frame->nb_samples,
                                    is->audio_frame->format, 1);*/
    			decoded_data_size = av_samples_get_buffer_size(NULL,
    				is->pCodecCtx->channels,
    				is->audio_frame->nb_samples,
    				AVSampleFormat(is->audio_frame->format), 1);//get decoded_data_size
    
              /*  dec_channel_layout = (is->audio_frame->channel_layout && is->audio_frame->channels
                                      == av_get_channel_layout_nb_channels(is->audio_frame->channel_layout))
                                     ? is->audio_frame->channel_layout
                                     : av_get_default_channel_layout(is->audio_frame->channels);*/
    
    			dec_channel_layout = (is->pCodecCtx->channel_layout && is->pCodecCtx->channels
    				== av_get_channel_layout_nb_channels(is->pCodecCtx->channel_layout))
    				? is->pCodecCtx->channel_layout
    				: av_get_default_channel_layout(is->pCodecCtx->channels);
    
                wanted_nb_samples =  is->audio_frame->nb_samples;
    
                //fprintf(stderr, "wanted_nb_samples = %d
    ", wanted_nb_samples);
                //解码出来的音频与原先设定的格式不一致,则重采样
                if (is->audio_frame->format != is->audio_src_fmt ||
                    dec_channel_layout != is->audio_src_channel_layout ||
                    is->pCodecCtx->sample_rate != is->audio_src_freq ||
                    (wanted_nb_samples != is->audio_frame->nb_samples && !is->swr_ctx)) {
                    if (is->swr_ctx) swr_free(&is->swr_ctx);
                    is->swr_ctx = swr_alloc_set_opts(NULL, //get swr_ctx
                                                     is->audio_tgt_channel_layout,
                                                     is->audio_tgt_fmt,
                                                     is->audio_tgt_freq,
                                                     dec_channel_layout,
                                                     AVSampleFormat(is->audio_frame->format),
                                                     is->pCodecCtx->sample_rate,
                                                     0, NULL);
                    if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {//init
                        fprintf(stderr, "swr_init() failed
    ");
                        break;
                    }//get src parameters
                    is->audio_src_channel_layout = dec_channel_layout;
                    is->audio_src_channels = is->audio_st->codec->channels;
                    is->audio_src_freq = is->audio_st->codec->sample_rate;
                    is->audio_src_fmt = is->audio_st->codec->sample_fmt;
                }
                if (is->swr_ctx) {  
                   // const uint8_t *in[] = { is->audio_frame->data[0] };
                    const uint8_t **in = (const uint8_t **)is->audio_frame->extended_data; 
                    uint8_t *out[] = { is->audio_buf2 };
    				if (wanted_nb_samples != is->audio_frame->nb_samples) {//compensate samples
    					 swr_compensate(is->swr_ctx, (wanted_nb_samples - is->audio_frame->nb_samples)
    												 * is->audio_tgt_freq / is->pCodecCtx->sample_rate,
    												 wanted_nb_samples * is->audio_tgt_freq / is->pCodecCtx->sample_rate);
    				 }
                     
                    len2 = swr_convert(is->swr_ctx, out,// in data is changed by swr_ctx
                                       sizeof(is->audio_buf2)
                                       / is->audio_tgt_channels
                                       / av_get_bytes_per_sample(is->audio_tgt_fmt),//tgt_nb_samples
                                       in, is->audio_frame->nb_samples);//convert
                    if (len2 < 0) {
                        fprintf(stderr, "swr_convert() failed
    ");
                        break;
                    }
                    if (len2 == sizeof(is->audio_buf2) / is->audio_tgt_channels / av_get_bytes_per_sample(is->audio_tgt_fmt)) {
                        fprintf(stderr, "warning: audio buffer is probably too small
    ");
                        swr_init(is->swr_ctx);
                    }
                    is->audio_buf = is->audio_buf2;//audio_buff
                    resampled_data_size = len2 * is->audio_tgt_channels * av_get_bytes_per_sample(is->audio_tgt_fmt);//resampled_data_size
                } else {
    				resampled_data_size = decoded_data_size;//not resampled
                    is->audio_buf = is->audio_frame->data[0];      
                }
                // We have data, return it and come back for more later
                return resampled_data_size;  //返回重采样后的长度
            }//decode one frame
    
            if (pkt->data) av_free_packet(pkt);
    		memset(pkt, 0, sizeof(*pkt));
            if (is->quit) return -1;
            if (packet_queue_get(&is->audioq, pkt, 1) < 0) return -1;//get next packet
    
            is->audio_pkt_data = pkt->data;
            is->audio_pkt_size = pkt->size;
        }
    }
    
    void audio_callback(void *userdata, Uint8 *stream, int len) {
        VideoState *is = (VideoState *)userdata;
        int len1, audio_data_size;
    	printf("audio callback 1 len=%d
    ",len);
    
        while (len > 0) {     
            if (is->audio_buf_index >= is->audio_buf_size) {
                audio_data_size = audio_decode_frame(is);//decode one frame,return size
                if(audio_data_size < 0) {
                    /* silence */
                    is->audio_buf_size = 1024;
                    memset(is->audio_buf, 0, is->audio_buf_size);
                } else {
                    is->audio_buf_size = audio_data_size;
                }
                is->audio_buf_index = 0;     
    			printf("audio callback 2 (audio_buf_size,audio_buf_index) = (%d,%d)
    ",is->audio_buf_size,is->audio_buf_index);
            }
                 
            len1 = is->audio_buf_size - is->audio_buf_index;
            if (len1 > len) {
                len1 = len;
            }
        
            memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1); 
            len -= len1;
            stream += len1;
            is->audio_buf_index += len1;
            printf("audio callback 3 (len1,len,audio_buf_index) = (%d,%d,%d)
    ",len1,len,is->audio_buf_index); 
    	}
    }
    
    int stream_component_open(VideoState *is, int stream_index) {//open stream 
        AVFormatContext *ic = is->ic;
        AVCodecContext *codecCtx;
        AVCodec *codec;
        SDL_AudioSpec wanted_spec, spec;
        int64_t wanted_channel_layout = 0;
        int wanted_nb_channels;
    	const int next_nb_channels[] = {0, 0, 1 ,6, 2, 6, 4, 6};
    
        if (stream_index < 0 || stream_index >= ic->nb_streams) {
            return -1;
        }
    	
        codecCtx = ic->streams[stream_index]->codec;
    	is->pCodecCtx=codecCtx;//Add
    	wanted_nb_channels = codecCtx->channels;//wanted parameters
    	if(!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
    		wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
    		wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
    	}
    	
    	wanted_spec.channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
    	wanted_spec.freq = codecCtx->sample_rate;
    	if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
    		fprintf(stderr, "Invalid sample rate or channel count!
    ");
    		return -1;
    	}
    	wanted_spec.format = AUDIO_S16SYS;
    	wanted_spec.silence = 0;
    	wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
    	wanted_spec.callback = audio_callback;//callback
    	wanted_spec.userdata = is;
    	
    	while(SDL_OpenAudio(&wanted_spec, &spec) < 0) {//OpenAudio
    		fprintf(stderr, "SDL_OpenAudio (%d channels): %s
    ", wanted_spec.channels, SDL_GetError());
    		wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
    		if(!wanted_spec.channels) {
    			fprintf(stderr, "No more channel combinations to tyu, audio open failed
    ");
    			return -1;
    		}
    		wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
    	}
    
    	if (spec.format != AUDIO_S16SYS) {
    		fprintf(stderr, "SDL advised audio format %d is not supported!
    ", spec.format);
    		return -1;
    	}
    	if (spec.channels != wanted_spec.channels) {
    		wanted_channel_layout = av_get_default_channel_layout(spec.channels);
    		if (!wanted_channel_layout) {
    			fprintf(stderr, "SDL advised channel count %d is not supported!
    ", spec.channels);
    			return -1;
    		}
    	}
    
    	fprintf(stderr, "%d: wanted_spec.format = %d
    ", __LINE__, wanted_spec.format);
    	fprintf(stderr, "%d: wanted_spec.samples = %d
    ", __LINE__, wanted_spec.samples);
    	fprintf(stderr, "%d: wanted_spec.channels = %d
    ", __LINE__, wanted_spec.channels);
    	fprintf(stderr, "%d: wanted_spec.freq = %d
    ", __LINE__, wanted_spec.freq);
    
    	fprintf(stderr, "%d: spec.format = %d
    ", __LINE__, spec.format);
    	fprintf(stderr, "%d: spec.samples = %d
    ", __LINE__, spec.samples);
    	fprintf(stderr, "%d: spec.channels = %d
    ", __LINE__, spec.channels);
    	fprintf(stderr, "%d: spec.freq = %d
    ", __LINE__, spec.freq);
        
    	is->audio_src_fmt = is->audio_tgt_fmt = AV_SAMPLE_FMT_S16;//src parameters
    	is->audio_src_freq = is->audio_tgt_freq = spec.freq;
    	is->audio_src_channel_layout = is->audio_tgt_channel_layout = wanted_channel_layout;
    	is->audio_src_channels = is->audio_tgt_channels = spec.channels;
        
        codec = avcodec_find_decoder(codecCtx->codec_id);//find decoder
    	is->pCodec=codec;//Add
        if (!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) {//Unsupported codec
            fprintf(stderr, "Unsupported codec!
    ");
            return -1;
        }
    	ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
        switch(codecCtx->codec_type) {
        case AVMEDIA_TYPE_AUDIO:
            is->audioStream = stream_index;
            is->audio_st = ic->streams[stream_index];
            is->audio_buf_size = 0;
            is->audio_buf_index = 0;
            memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
            packet_queue_init(&is->audioq);
            SDL_PauseAudio(0);
            break;
        default:
            break;
        }
    }
    /*
    static void stream_component_close(VideoState *is, int stream_index) {
    	AVFormatContext *oc = is->;
    	AVCodecContext *avctx;
    
    	if(stream_index < 0 || stream_index >= ic->nb_streams)	return;
    	avctx = ic->streams[stream_index]->codec;
    
    }
    */
    static int decode_thread(void *arg) {
    	//初始化参数,函数内部处理得到的相关参数赋给 is
        VideoState *is = (VideoState *)arg;
        AVFormatContext *ic = NULL;
        AVPacket pkt1, *packet = &pkt1;
        int ret, i, audio_index = -1;
    
        is->audioStream=-1;
        global_video_state = is;//全局状态
        if (avformat_open_input(&ic, is->filename, NULL, NULL) != 0) {
            return -1;
        }
        is->ic = ic;
        if (avformat_find_stream_info(ic, NULL) < 0) {//打开流信息
            return -1;
        }
        av_dump_format(ic, 0, is->filename, 0);
        for (i=0; i<ic->nb_streams; i++) {
            if (ic->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && audio_index < 0) {//打开第一条音频流
                audio_index=i;
                break;
            }
        }
        if (audio_index >= 0) {
            stream_component_open(is, audio_index);//打开音频流
        }
        if (is->audioStream < 0) {
            fprintf(stderr, "%s: could not open codecs
    ", is->filename);
            goto fail;
        }
        // main decode loop
        for(;;) {
            if(is->quit) break;
            if (is->audioq.size > MAX_AUDIOQ_SIZE) {
                SDL_Delay(10);//so fast
                continue;
            }
            ret = av_read_frame(is->ic, packet);//read data to one packet
            if (ret < 0) {
                if(ret == AVERROR_EOF || url_feof(is->ic->pb)) {//error or end
                    break;
                }
                if(is->ic->pb && is->ic->pb->error) {
                    break;
                }
                continue;
            }
    
            if (packet->stream_index == is->audioStream) {//packet data to audioq   
                packet_queue_put(&is->audioq, packet);
            } else {
                av_free_packet(packet);
            }
        }
    
        while (!is->quit) {// delay
            SDL_Delay(100); 
        }
    
    fail: {//if  fail
            SDL_Event event;
            event.type = FF_QUIT_EVENT;
            event.user.data1 = is;
            SDL_PushEvent(&event);
        }
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        SDL_Event       event;
        VideoState      *is;
    
        is = (VideoState *)av_mallocz(sizeof(VideoState));
    
        //if (argc < 2) {
        //    fprintf(stderr, "Usage: test <file>
    ");
        //    exit(1);
        //}
    
        argv[1]="test.mp4";
        av_register_all();//注册编解码库
    
        if (SDL_Init(SDL_INIT_AUDIO)) { //初始化音频SDL
            fprintf(stderr, "Could not initialize SDL - %s
    ", SDL_GetError());
            exit(1);
        }
            
        av_strlcpy(is->filename,argv[1], sizeof(is->filename));
    
        is->parse_tid = SDL_CreateThread(decode_thread, is);
        if (!is->parse_tid) {
            av_free(is);
            return -1;
        }
    
        for(;;) {
            SDL_WaitEvent(&event);
            switch(event.type) {
            case FF_QUIT_EVENT:
            case SDL_QUIT://退出
                is->quit = 1;
                SDL_Quit();
                exit(0);
                break;
            default:
                break;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    JSON特殊字符的处理
    java中高并发和高响应解决方法
    对redis深入理解
    对java中arraylist深入理解
    Redis的字典扩容与ConcurrentHashMap的扩容策略比较
    PHP压缩上传图片
    windows 平台 php_Imagick 拓展遇到的那些坑!
    windows7下php5.4成功安装imageMagick,及解决php imagick常见错误问题。(phpinfo中显示不出来是因为:1.imagick软件本身、php本身、php扩展三方版本要一致,2.需要把CORE_RL_*.dll多个文件放到/php/目录下面)
    php使用imagick模块实现图片缩放、裁剪、压缩示例
    文件打包,下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件
  • 原文地址:https://www.cnblogs.com/welen/p/3666247.html
Copyright © 2011-2022 走看看