#include <stdio.h> #include <stdlib.h> extern "C" { #include <SDL.h> #include "libavutil/opt.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/mathematics.h" #include "libavutil/samplefmt.h" #include "libavutil/time.h" #include "libavutil/fifo.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavformat/avio.h" #include "libavfilter/avfiltergraph.h" #include "libavfilter/avfilter.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" #include "libswscale/swscale.h" #include "libswresample/swresample.h" } #include <memory> #include <windows.h> #include "sdlplayer.h" AVInputFormat mFormat; AVDictionary* iformat_opts; using namespace std; int audioIndex = 0; SwrContext* swr; AVFormatContext *inputContext = NULL; int64_t lastReadPacktTime ; int mymain(); //extern "C" _declspec(dllexport) int mymain(); int _tmain(int argc, _TCHAR* argv[]){ mymain(); return 0; } void Init() { av_register_all(); avfilter_register_all(); avformat_network_init(); av_log_set_level(AV_LOG_ERROR); } std::shared_ptr <AVPacket> readPacketFromSource() { std::shared_ptr<AVPacket> packet(static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))), [&](AVPacket *p) { av_packet_free(&p); av_freep(&p);}); av_init_packet(packet.get()); lastReadPacktTime = av_gettime(); int ret = av_read_frame(inputContext, packet.get()); if(ret >= 0) { return packet; } else { return nullptr; } } int initAudioDecodeContext() { int ret = -1; auto codecId = inputContext->streams[audioIndex]->codec->codec_id; auto codec = avcodec_find_decoder(codecId); if (!codec) { return ret; } ret = avcodec_open2(inputContext->streams[audioIndex]->codec, codec, NULL); if(ret < 0) return ret; if(inputContext->streams[audioIndex]->codec->sample_fmt != AV_SAMPLE_FMT_S16) { swr = swr_alloc(); av_opt_set_int(swr, "in_channel_layout", inputContext->streams[audioIndex]->codec->channel_layout, 0); av_opt_set_int(swr, "out_channel_layout", inputContext->streams[audioIndex]->codec->channel_layout, 0); av_opt_set_int(swr, "in_sample_rate", inputContext->streams[audioIndex]->codec->sample_rate, 0); av_opt_set_int(swr, "out_sample_rate", inputContext->streams[audioIndex]->codec->sample_rate, 0); av_opt_set_sample_fmt(swr, "in_sample_fmt", inputContext->streams[audioIndex]->codec->sample_fmt, 0); av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); swr_init(swr); } return ret; } bool audioDecode(AVPacket* packet, AVFrame *frame) { int gotFrame = 0; auto hr = avcodec_decode_audio4(inputContext->streams[audioIndex]->codec, frame, &gotFrame, packet); if (hr >= 0 && gotFrame != 0) { return true; } return false; } std::shared_ptr<CGSDLRender> sdlRender =std::make_shared<CGSDLRender>(); void playAudio(AVFrame* frame) { if(swr) { int dstNbChannels = 1; int srcNbSamples = frame->nb_samples; int srcRate =inputContext->streams[audioIndex]->codec->sample_rate; int dstRate = inputContext->streams[audioIndex]->codec->sample_rate; int dstNbSamples = av_rescale_rnd(srcNbSamples, dstRate, srcRate, AV_ROUND_UP); AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16; uint8_t** dst_data = nullptr; int dstLinesize; dstNbChannels = av_get_channel_layout_nb_channels(inputContext->streams[0]->codec->channel_layout); dstNbChannels = dstNbChannels > 0 ? dstNbChannels : 1; int ret = av_samples_alloc_array_and_samples(&dst_data, &dstLinesize, dstNbChannels, dstNbSamples, dst_sample_fmt, 0); ret = swr_convert(swr, dst_data, dstNbSamples, (const uint8_t **)frame->data, srcNbSamples); sdlRender->PlaySamples((BYTE*)dst_data[0], dstLinesize); if (dst_data) { av_freep((void*)&dst_data[0]); } av_freep(&dst_data); } else { sdlRender->PlaySamples(frame->data[0], frame->linesize[0]); } } int mymain() { int scan_all_pmts_set = 0; /* register all codecs, demux and protocols */ Init(); inputContext = avformat_alloc_context(); int ret; if (!inputContext) { av_log(NULL, AV_LOG_FATAL, "Could not allocate context. "); ret = AVERROR(ENOMEM); printf("alloc err %d ",ret); } //int err = avformat_open_input(&inputContext, "F://output.m4a", nullptr, nullptr); int err = avformat_open_input(&inputContext, "F://test.mp4", nullptr, nullptr); //int err = avformat_open_input(&inputContext, "F://3s.mp4", nullptr, nullptr); //int err = avformat_open_input(&inputContext, "F://sax.mp3", nullptr, nullptr); //int err = avformat_open_input(&inputContext, "F://Alarm01.wav", nullptr, nullptr); if (err < 0) { printf("open err err=%d ",err); } err = avformat_find_stream_info(inputContext, nullptr); printf("ic->nb_streams %d ",inputContext->nb_streams); if(err<0){ printf("Find input file stream inform failed "); }else{ for(int i=0;i<inputContext->nb_streams;i++){ int type = inputContext->streams[i]->codec->codec_type; printf("type = %d ",type); if(inputContext->streams[i]->codec->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO) { audioIndex = i; } } } ret = initAudioDecodeContext(); if(ret < 0) return ret; sdlRender->InitAudio(inputContext->streams[audioIndex]->codec->sample_rate,inputContext->streams[audioIndex]->codec->channels); AVFrame * videoFrame = av_frame_alloc(); for(int i=0;;i++){ auto packet = readPacketFromSource(); if(packet){ if(packet->stream_index==audioIndex) if(audioDecode(packet.get(),videoFrame)){ printf("packet->pts time =%d ",1000*packet->pts/(inputContext->streams[audioIndex]->time_base.den /inputContext->streams[audioIndex]->time_base.num)); playAudio(videoFrame); //Sleep(1); } } else{ printf("i=%d Sleep---> ",i); break; } } printf("Sleep---> "); Sleep(20000); av_frame_free(&videoFrame); return 0; }
sdlplayer.h
#ifndef SDL_PLAYER_H #define SDL_PLAYER_H #include <SDL.h> #include <windows.h> #include <mutex> class CGBlockRingBuffer { public: CGBlockRingBuffer() :buffer(nullptr) ,bufferSize(0) ,writeIndex(0) ,readIndex(0) ,bytesCanRead(0) { } void Init(int buffSize) { buffer = new uint8_t [buffSize]; if(!buffer) return; this->bufferSize = buffSize; } void Write(uint8_t *src, int size) { if(!src ||size == 0) return ; std::lock_guard<std::mutex> lk(lock); int bytesCanWrite = size < bufferSize - bytesCanRead ? size : (bufferSize - bytesCanRead); if(bytesCanWrite <= bufferSize - writeIndex) { memcpy(buffer + writeIndex,src,bytesCanWrite); writeIndex += bytesCanWrite; if(writeIndex == bufferSize) { writeIndex = 0; } } else { int room = bufferSize - writeIndex; memcpy(buffer + writeIndex, src,room); int left = bytesCanWrite - room; memcpy(buffer, src + room, left); writeIndex = left; } bytesCanRead += bytesCanWrite; } int Read(uint8_t *dst, int size) { if(!dst || size == 0) return 0; std::lock_guard<std::mutex> lk(lock); int bytesRead = size < bytesCanRead ? size : bytesCanRead; if(bytesRead <= bufferSize - readIndex) { memcpy(dst,buffer + readIndex,bytesRead); readIndex += bytesRead; if(readIndex == bufferSize) readIndex = 0; } else { int bytesHead = bufferSize - readIndex; memcpy(dst, buffer + readIndex,bytesHead); int bytesTail = bytesRead - bytesHead; memcpy(dst + readIndex,buffer, bytesTail); readIndex = bytesTail; } bytesCanRead -= bytesRead; return bytesRead; } private: uint8_t *buffer; int bufferSize; int readIndex; int writeIndex; int bytesCanRead; std::mutex lock; }; class CGSDLRender { public: CGSDLRender(); virtual ~CGSDLRender(void); int InitAudio(int samples,int channels); int PlaySamples(BYTE* buffer, int size) ; void Close(); CGBlockRingBuffer *blockingBuffer; private: }; #endif
sdlplayer.cpp
#include <SDL.h> #include <windows.h> #include "sdlplayer.h" void callback(void *userdata, Uint8 *stream, int len) { SDL_memset(stream, 0, len); if(userdata != nullptr) { CGBlockRingBuffer* blockingBuffer = (CGBlockRingBuffer*)userdata; auto len1 = 0; while (len > 0) { len1 = blockingBuffer->Read((uint8_t*)stream + len1,len); len = len - len1; //printf("len1 len %d,%d ",len1,len); } } } CGSDLRender::~CGSDLRender() { } CGSDLRender::CGSDLRender() { } void CGSDLRender::Close() { SDL_CloseAudio(); if(blockingBuffer) { delete blockingBuffer; } } int CGSDLRender::InitAudio(int samples,int channels) { SDL_Init(SDL_INIT_AUDIO); blockingBuffer = new CGBlockRingBuffer(); blockingBuffer->Init(4*1024 * 1024); SDL_AudioSpec wanted; wanted.freq = samples; wanted.format = AUDIO_S16SYS; wanted.channels = channels > 2 ? 2 : channels; wanted.silence = 0; wanted.samples = 1024; wanted.callback = callback; wanted.userdata = this->blockingBuffer; // devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wanted, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE); auto r = (SDL_OpenAudio(&wanted,NULL)); SDL_PauseAudio(0); return r; } int CGSDLRender::PlaySamples(BYTE* buffer, int size) { this->blockingBuffer->Write(buffer,size); //printf("size = %d ",size); //SDL_QueueAudio(1,buffer,size); return S_OK; }