sdltest1.cpp
// sdltest1.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #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 <vector> #include <windows.h> #include <thread> #include <mutex> #include <queue> #include <iostream> #include "sdlplayer.h" #pragma comment(lib, "avcodec.lib") using namespace std; //int mymain(); //extern "C" _declspec(dllexport) int mymain(); int mymain(char* file); //int _tmain(int argc, _TCHAR* argv[]) int main(int argc, char* argv[]) { printf("come this --> "); if(argc!=2) { printf("Args count not right! "); //return 0; } printf("%s ",(char*)argv[1]); mymain((char*)argv[1]); return 0; } #define INBUF_SIZE 4096 #define DelayTime 5 typedef struct { std::shared_ptr<BYTE> buf; BYTE* data[3]; int linesize[AV_NUM_DATA_POINTERS]; int64_t dts; } DecodeVideoFrame; typedef struct { std::shared_ptr<BYTE> buf; BYTE* data; int linesize; int startPos; double dpts; } DecodeAudioFrame; int videoIndex = -1; int audioIndex = -1; AVInputFormat mFormat; AVDictionary* iformat_opts; AVFormatContext *ic = NULL; int64_t lastReadPacktTime; queue<std::shared_ptr<DecodeVideoFrame>> quePicFrame; queue<std::shared_ptr<DecodeAudioFrame>> queSmpFrame; int gWidth = 0; int gHeight = 0; int playState = 1; std::shared_ptr<CGSDLRender> sdlRender; mutex g_lock; int64_t lastDts = 0; unsigned long lastTime = 0; SwrContext* swr; double audio_clock = 0.; double video_clock = 0.; void fill_audio(void *userdata, Uint8 *stream, int len) { SDL_memset(stream, 0, len); if (queSmpFrame.size() == 0) return; if (video_clock <= 0.01) return; std::shared_ptr<DecodeAudioFrame> daf = queSmpFrame.front(); int leftLen = daf->linesize - daf->startPos; byte* buf = new byte[len]; memset(buf,0,len); int startPos = 0; //daf->data if (len >= leftLen){ memcpy(buf, daf->data + daf->startPos, leftLen); queSmpFrame.pop(); int newlen = len - leftLen; //printf("newlen = %d --> ", newlen); audio_clock = daf->dpts; if (newlen > 0){ auto daf2 = daf = queSmpFrame.front(); memcpy(buf + leftLen, daf2->data, newlen); daf2->startPos = newlen; //printf("audio pts %.1f",daf2->dpts); audio_clock = daf2->dpts; } else{ //printf("audio pts %.1f", daf->dpts); } } else{ memcpy(buf, daf->data + daf->startPos, len); daf->startPos = daf->startPos + len; //printf("audio pts %.1f", daf->dpts); audio_clock = daf->dpts; } SDL_MixAudio(stream, buf, len, SDL_MIX_MAXVOLUME); //daf->startPos } 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(ic, packet.get()); if(ret >= 0){ return packet; }else{ return nullptr; } } bool videoDecode(AVPacket* packet, AVFrame *frame){ int gotFrame = 0; auto hr = avcodec_decode_video2(ic->streams[videoIndex]->codec, frame, &gotFrame, packet); if (hr >= 0 && gotFrame != 0){ return true; } return false; } int initVideoDecodeContext(){ auto codecId = ic->streams[videoIndex]->codec->codec_id; auto codec = avcodec_find_decoder(codecId); if (!codec){ return -1; } int ret = avcodec_open2(ic->streams[videoIndex]->codec, codec, NULL); return ret; } int initAudioDecodeContext() { int ret = -1; auto codecId = ic->streams[audioIndex]->codec->codec_id; auto codec = avcodec_find_decoder(codecId); if (!codec) { return ret; } ret = avcodec_open2(ic->streams[audioIndex]->codec, codec, NULL); if (ret < 0) return ret; if (ic->streams[audioIndex]->codec->sample_fmt != AV_SAMPLE_FMT_S16) { swr = swr_alloc(); av_opt_set_int(swr, "in_channel_layout", ic->streams[audioIndex]->codec->channel_layout, 0); av_opt_set_int(swr, "out_channel_layout", ic->streams[audioIndex]->codec->channel_layout, 0); av_opt_set_int(swr, "in_sample_rate", ic->streams[audioIndex]->codec->sample_rate, 0); av_opt_set_int(swr, "out_sample_rate", ic->streams[audioIndex]->codec->sample_rate, 0); av_opt_set_sample_fmt(swr, "in_sample_fmt", ic->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(ic->streams[audioIndex]->codec, frame, &gotFrame, packet); if (hr >= 0 && gotFrame != 0) { return true; } return false; } void addAudioData(BYTE* buffer, int size, AVFrame* frame, double dPts){ if (NULL == frame) return; std::shared_ptr<DecodeAudioFrame> dfr(static_cast<DecodeAudioFrame*>(new DecodeAudioFrame), [&](DecodeAudioFrame *p) { delete p; }); std::shared_ptr<BYTE> tmpbuf(new BYTE[size](), std::default_delete<BYTE[]>()); dfr->buf = tmpbuf; dfr->data = dfr->buf.get(); memcpy(dfr->data, buffer, size); dfr->linesize = size; dfr->startPos = 0; dfr->dpts = dPts; queSmpFrame.push(dfr); } void prepareAudioData(AVFrame* frame, double dPts) { if (swr) { int dstNbChannels = 1; int srcNbSamples = frame->nb_samples; int srcRate = ic->streams[audioIndex]->codec->sample_rate; int dstRate = ic->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(ic->streams[audioIndex]->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); addAudioData((BYTE*)dst_data[0], dstLinesize, frame, dPts); if (dst_data){ av_freep((void*)&dst_data[0]); } av_freep(&dst_data); } else { addAudioData(frame->data[0], frame->linesize[0], frame, dPts); } } void playFun(){ for(;;){ if(playState == 0) break; if (quePicFrame.size()>0){ g_lock.lock(); std::shared_ptr<DecodeVideoFrame> dfr = quePicFrame.front(); quePicFrame.pop(); g_lock.unlock(); auto diff = dfr->dts - lastDts; int duration = diff * 1000 /(ic->streams[videoIndex]->time_base.den /ic->streams[videoIndex]->time_base.num); if(duration > DelayTime && duration < 1000){ //Sleep(duration ); } //std::cout<<"duration1: "<<duration<<endl; video_clock = dfr->dts * 1000 / (ic->streams[videoIndex]->time_base.den / ic->streams[videoIndex]->time_base.num); int diff2 = video_clock - audio_clock; printf("v %.1f a %.1f ---> ", video_clock, audio_clock); if (diff2 > 100){ Sleep(100); }else if(diff2 > 0){ Sleep(duration); }else if (diff2 < 0){ continue; } printf("video pts =%.1f ---> ", video_clock); sdlRender->Display((char**)dfr->data, dfr->linesize); lastDts = dfr->dts; unsigned long nowTime = GetTickCount(); //这个需要释放,to thi--------??? lastTime = nowTime; }else{ Sleep(150); } } } int mymain(char* file){ int scan_all_pmts_set = 0; /* register all codecs, demux and protocols */ Init(); ic = avformat_alloc_context(); int ret; if (!ic) { av_log(NULL, AV_LOG_FATAL, "Could not allocate context. "); ret = AVERROR(ENOMEM); printf("alloc err %d ",ret); } //"F://3s.mp4" int err = avformat_open_input(&ic, "F://test.mp4", nullptr, nullptr); if (err < 0) { printf("open err err=%d ",err); } err = avformat_find_stream_info(ic, nullptr); printf("ic->nb_streams %d ",ic->nb_streams); if(err<0){ }else{ for(int i=0;i<ic->nb_streams;i++){ int type = ic->streams[i]->codec->codec_type; printf("type = %d ",type); if(type == AVMediaType::AVMEDIA_TYPE_VIDEO){ videoIndex = i; printf("videoIndex =%d ",videoIndex); } else if (type == AVMediaType::AVMEDIA_TYPE_AUDIO){ audioIndex = i; } } } gWidth = ic->streams[videoIndex]->codec->width; gHeight = ic->streams[videoIndex]->codec->height; if (videoIndex >= 0) { int ret1 = initVideoDecodeContext(); printf("ret1 = %d ", ret1); //std::shared_ptr<CGSDLRender> sdlRender = std::make_shared<CGSDLRender>();//??????? ret = initVideoDecodeContext(); if (ret < 0) return ret; sdlRender->InitVideo(0); } if (audioIndex >= 0) { ret = initAudioDecodeContext(); if (ret < 0) return ret; sdlRender->InitAudio(ic->streams[audioIndex]->codec->sample_rate, ic->streams[audioIndex]->codec->channels); } sdlRender->CreateVideoSurface(gWidth, gHeight); AVRational time_base =ic->streams[videoIndex]->time_base; printf("num %d,den %d-- ",time_base.num,time_base.den); playState = 1; thread t1(playFun); t1.detach(); int64_t lastDts=0; int w = gWidth; int h = gHeight; AVFrame * videoFrame = av_frame_alloc(); for(int i=0;i<10000;i++){ auto packet = readPacketFromSource(); if(packet){ if(packet->stream_index==videoIndex){ if(videoDecode(packet.get(),videoFrame)) { printf("%d--- ",i); std::shared_ptr<DecodeVideoFrame> dfr(static_cast<DecodeVideoFrame*>(new DecodeVideoFrame), [&](DecodeVideoFrame *p) { delete p; }); std::shared_ptr<BYTE> tmpbuf (new BYTE[w * h * 3 / 2 + 100](), std::default_delete<BYTE[]>()); dfr->buf = tmpbuf; memcpy(dfr->buf.get(),videoFrame->data[0],w*h); memcpy(dfr->buf.get() + w * h, videoFrame->data[1], w * h / 4); memcpy(dfr->buf.get() + w * h * 5 / 4, videoFrame->data[2], w * h / 4); dfr->data[0] = dfr->buf.get(); dfr->data[1] = dfr->buf.get() + w * h; dfr->data[2] = dfr->buf.get() + w * h * 5 / 4; dfr->dts = packet->dts; memcpy(dfr->linesize, videoFrame->linesize, AV_NUM_DATA_POINTERS*sizeof(int)); g_lock.lock(); quePicFrame.push(dfr); g_lock.unlock(); /* printf("packet->dts time =%d ",dfr->dts*1000/(ic->streams[videoIndex]->time_base.den /ic->streams[videoIndex]->time_base.num));*/ if (quePicFrame.size()>30) Sleep(100); } } else if (packet->stream_index == audioIndex) { if (audioDecode(packet.get(), videoFrame)){ int64_t pts = packet->pts; double dpts = pts * 1000 / (ic->streams[audioIndex]->time_base.den / ic->streams[audioIndex]->time_base.num); prepareAudioData(videoFrame, dpts); } } } else{ break; } } av_frame_free(&videoFrame); Sleep(8000); playState = 0; Sleep(600); system("PAUSE"); return 0; }
sdlplayer.h
#include <mutex> class CGSDLRender { public: CGSDLRender(); virtual ~CGSDLRender(void); int InitVideo( int adapterId = 0); int CreateVideoSurface(int width, int height); int Display(char** data, int* linesize); int InitAudio(int samples, int channels); //int PlaySamples(BYTE* buffer, int size); void Close(); private: SDL_Window *sdlWindow; SDL_Renderer *sdlRender; SDL_Texture *sdlTexture; int width; int height; //CGBlockRingBuffer *blockingBuffer; private: };
sdlplayer.cpp
#include <SDL.h> #include <windows.h> #include "sdlplayer.h" extern void fill_audio(void *userdata, Uint8 *stream, int len); CGSDLRender::~CGSDLRender() { } CGSDLRender::CGSDLRender() //:blockingBuffer(nullptr) { } int CGSDLRender::InitVideo( int adapterId) { SDL_Init(SDL_INIT_VIDEO); sdlWindow = SDL_CreateWindow(("窗口标题"),500,790,150,150,SDL_WINDOW_SHOWN); sdlRender = SDL_CreateRenderer(sdlWindow, -1, SDL_RendererFlags::SDL_RENDERER_ACCELERATED); return S_OK; } int CGSDLRender::CreateVideoSurface(int width, int height) { this->width = width; this->height = height; sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, width, height); return S_OK; } int CGSDLRender::Display(char** data, int* linesize) { void* piexels = nullptr; int pitch; int ret = SDL_LockTexture(sdlTexture, NULL, &piexels, &pitch); if(ret < 0) return ret; uint8_t* yuv[3] = { (uint8_t*)piexels,(uint8_t*)piexels + pitch * height, (uint8_t*)piexels + pitch * height + ((pitch >> 1) * (height >> 1)) }; for (int i = 0; i < height; i++) { memcpy(yuv[0] + i * pitch, data[0] + i * linesize[0], linesize[0]); if (i % 2 == 0) { memcpy(yuv[1] + (i >> 1) * (pitch >> 1), data[2] + (i >> 1) * linesize[2], linesize[2]); memcpy(yuv[2] + (i >> 1) * (pitch >> 1), data[1] + (i >> 1) * linesize[1], linesize[1]); } } SDL_UnlockTexture(sdlTexture); SDL_RenderClear(sdlRender); SDL_RenderCopy(sdlRender, sdlTexture, NULL, NULL); SDL_RenderPresent(sdlRender); return S_OK; } void CGSDLRender::Close() { SDL_CloseAudio(); } int CGSDLRender::InitAudio(int samples, int channels) { SDL_Init(SDL_INIT_AUDIO); SDL_AudioSpec wanted; wanted.freq = samples; wanted.format = AUDIO_S16SYS; wanted.channels = channels > 2 ? 2 : channels; wanted.silence = 0; wanted.samples = 1024; wanted.callback = fill_audio; auto r = (SDL_OpenAudio(&wanted, NULL)); SDL_PauseAudio(0); return r; }