C++播放RTMP源代码-RTMPClient C++
来源:互联网 发布:网络金融mba智库 编辑:程序博客网 时间:2024/05/19 00:40
根据大牛雷骁骅给出的代码修改得到一个既支持音频又支持视频的播放器。
(最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)http://blog.csdn.net/leixiaohua1020/article/details/38979615#cpp)
修改内容:
1 将 视频例子和音频例子进行了合并
2 使用循环缓冲区替换了原本的缓冲机制(从网上找的缓冲区代码,抱歉忘记从哪里拷贝的了,不然一定会注明的)
3 尝试播放了 rtmp
char url[] = "rtmp://live.hkstv.hk.lxdns.com/live/hks live=1";
4 修正了声音有断续杂音的情况。 关键是使用swr_convert转换声音后,声音数据的大小是swr_convert*声道数*每帧数据字节数
// myffplayer.cpp : 定义控制台应用程序的入口点。//#include <stdio.h>#define __STDC_CONSTANT_MACROSextern "C"{#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libswscale/swscale.h"#include "SDL/SDL.h"#include <libswresample/swresample.h>};#include "CycleBuffer.h"//Refresh#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audioint thread_exit=0;//Threadint sfp_refresh_thread(void *opaque){SDL_Event event;while (thread_exit==0) {event.type = SFM_REFRESH_EVENT;SDL_PushEvent(&event);//Wait 40 msSDL_Delay(40);}return 0;}//Buffer://|-----------|-------------|//chunk-------pos---len-----|static Uint8 *audio_chunk; static Uint32 audio_len; static Uint8 *audio_pos; uint8_t*copy_buf;CCycleBuffer* pSoundBuf;/* The audio function callback takes the following parameters: * stream: A pointer to the audio buffer to be filled * len: The length (in bytes) of the audio buffer */ void fill_audio(void *udata,Uint8 *stream,int len){ //SDL 2.0SDL_memset(stream, 0, len);/*if(audio_len==0)return; len=(len>audio_len?audio_len:len); SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);audio_pos += len; audio_len -= len; */int n =pSoundBuf->Read((char*)copy_buf,len);SDL_MixAudio(stream,copy_buf,n,SDL_MIX_MAXVOLUME);} int main(int argc, char* argv[]){pSoundBuf=new CCycleBuffer(192000*10);int ret;//注册全部插件av_register_all();//分配内存AVFormatContext* fctx = avformat_alloc_context();//打开输入流AVFrame* f = av_frame_alloc();avformat_network_init();printf("%s",avcodec_configuration());char filepath[]= "rtmp://live.hkstv.hk.lxdns.com/live/hks live=1";ret = avformat_open_input(&fctx,filepath,NULL,NULL);if(ret != 0){printf("Couldn't open input stream.\n");exit(2);}ret = avformat_find_stream_info(fctx,NULL);if(ret < 0){printf("can't find stream info ");exit(3);}av_dump_format(fctx, 0, filepath, false);//查找视频流和音频流的编号int video_stream,audio_stream;int find_n = 0;for(int i=0;i<fctx->nb_streams;i++){if(fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){video_stream = i;find_n ++;printf("find video stream id %d",video_stream);}if(fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){audio_stream = i;find_n ++;printf("find audio stream id %d",audio_stream);}if(find_n >= 2){break;}}//获取解码上下文和解码器AVCodecContext*video_codec_ctx = fctx->streams[video_stream]->codec;AVCodec*video_codec = avcodec_find_decoder(video_codec_ctx->codec_id);;AVCodecContext*audio_codec_ctx = fctx->streams[audio_stream]->codec;AVCodec*audio_codec = avcodec_find_decoder(audio_codec_ctx->codec_id);if(avcodec_open2(video_codec_ctx, video_codec,NULL)<0){printf("Could not open video codec.\n");return -1;}if(avcodec_open2(audio_codec_ctx, audio_codec,NULL)<0){printf("Could not open audio codec.\n");return -1;}//初始化frameAVFrame*pFrame,*pFrameYUV, *audioFrame;AVPacket *packet;pFrame=av_frame_alloc();audioFrame = av_frame_alloc();pFrameYUV=av_frame_alloc();packet=(AVPacket *)av_malloc(sizeof(AVPacket));av_init_packet(packet);//初始化SDLif(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1;} //初始化显示相关int screen_w = video_codec_ctx->width;int screen_h = video_codec_ctx->height;SDL_Surface *screen; screen = SDL_SetVideoMode(screen_w, screen_h, 0,0);SDL_Overlay *bmp = SDL_CreateYUVOverlay(screen_w, screen_h,SDL_YV12_OVERLAY, screen); SDL_Rect rect;rect.x = 0; rect.y = 0; rect.w = screen_w; rect.h = screen_h; SDL_WM_SetCaption("Simple FFmpeg Player (SDL Update)",NULL);struct SwsContext *img_convert_ctx = sws_getContext(video_codec_ctx->width, video_codec_ctx->height, video_codec_ctx->pix_fmt, video_codec_ctx->width, video_codec_ctx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //初始化音频相关//Out Audio Paramuint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;//nb_samples: AAC-1024 MP3-1152int out_nb_samples=audio_codec_ctx->frame_size;AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;int out_sample_rate=44100;int out_channels=av_get_channel_layout_nb_channels(out_channel_layout);//Out Buffer Sizeint out_buffer_size=av_samples_get_buffer_size(NULL,out_channels ,out_nb_samples,out_sample_fmt, 1);uint8_t *out_buffer=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);copy_buf=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);//SDL_AudioSpecSDL_AudioSpec wanted_spec;wanted_spec.freq = out_sample_rate; wanted_spec.format = AUDIO_S16SYS; wanted_spec.channels = out_channels; wanted_spec.silence = 0; wanted_spec.samples = out_nb_samples; wanted_spec.callback = fill_audio; wanted_spec.userdata = audio_codec_ctx; if (SDL_OpenAudio(&wanted_spec, NULL)<0){ printf("can't open audio.\n"); return -1; } int64_t in_channel_layout=av_get_default_channel_layout(audio_codec_ctx->channels);//Swrstruct SwrContext *au_convert_ctx = swr_alloc();au_convert_ctx=swr_alloc_set_opts(au_convert_ctx,out_channel_layout, out_sample_fmt, out_sample_rate,in_channel_layout,audio_codec_ctx->sample_fmt , audio_codec_ctx->sample_rate,0, NULL);swr_init(au_convert_ctx);//创建消息线程SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread,NULL);////消息循环SDL_Event e;int got;while(true) {//WaitSDL_WaitEvent(&e);if(e.type==SFM_REFRESH_EVENT){//------------------------------SDL_PauseAudio(0);while(true){if(av_read_frame(fctx, packet)>=0){if(packet->stream_index==video_stream){ret = avcodec_decode_video2(video_codec_ctx, pFrame, &got, packet);if(ret < 0){printf("Decode Error.\n");return -1;}if(got){SDL_LockYUVOverlay(bmp);pFrameYUV->data[0]=bmp->pixels[0];pFrameYUV->data[1]=bmp->pixels[2];pFrameYUV->data[2]=bmp->pixels[1]; pFrameYUV->linesize[0]=bmp->pitches[0];pFrameYUV->linesize[1]=bmp->pitches[2]; pFrameYUV->linesize[2]=bmp->pitches[1];sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, video_codec_ctx->height, pFrameYUV->data, pFrameYUV->linesize);for(int i=0;i<5000;i++){bmp->pixels[0][i] = 0xff;}SDL_UnlockYUVOverlay(bmp); SDL_DisplayYUVOverlay(bmp, &rect); av_free_packet(packet);break;}}else if(packet->stream_index == audio_stream){ret = avcodec_decode_audio4( audio_codec_ctx, audioFrame,&got, packet);if(got >0){int rr = swr_convert(au_convert_ctx,&out_buffer, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)audioFrame->data , audioFrame->nb_samples);pSoundBuf->Write((char*)out_buffer,rr*4);}}av_free_packet(packet);}else{//Exit Threadthread_exit=1;break;}}}}SDL_CloseAudio();//Close SDLSDL_Quit();sws_freeContext(img_convert_ctx);//--------------//av_free(out_buffer);av_free(pFrameYUV);av_free(out_buffer);swr_free(&au_convert_ctx);avcodec_close(video_codec_ctx);avcodec_close(audio_codec_ctx);avformat_close_input(&fctx);getchar();return 0;}
循环缓冲区代码
#ifndef __test__CCycleBuffer__ #define __test__CCycleBuffer__ #include <iostream> #include <assert.h> class CCycleBuffer { public: bool isFull(); bool isEmpty(); void Empty(); int GetLength(); CCycleBuffer(int size); virtual ~CCycleBuffer(); int Write(char* buf, int count); int Read(char* buf, int count); private: bool m_bEmpty, m_bFull; char * m_pBuf; int m_nBufSize; int m_nReadPos; int m_nWritePos; public: int GetReadPos() { return m_nReadPos; } int GetWritePos() { return m_nWritePos; } }; #endif /* defined(__test__CCycleBuffer__) */
#include "CycleBuffer.h"// 定义CCycleBuffer::CCycleBuffer(int size){ m_nBufSize = size; m_nReadPos = 0; m_nWritePos = 0; m_pBuf = new char[m_nBufSize]; m_bEmpty = true; m_bFull = false;}CCycleBuffer::~CCycleBuffer(){ delete[] m_pBuf;}/************************************************************************//* 向缓冲区写入数据,返回实际写入的字节数 *//************************************************************************/int CCycleBuffer::Write(char* buf, int count){ if(count <= 0) return 0; m_bEmpty = false; // 缓冲区已满,不能继续写入 if(m_bFull) { return 0; } else if(m_nReadPos == m_nWritePos) // 缓冲区为空时 { /* == 内存模型 == (empty) m_nReadPos (empty) |----------------------------------|-----------------------------------------| m_nWritePos m_nBufSize */ int leftcount = m_nBufSize - m_nWritePos; if(leftcount > count) { memcpy(m_pBuf + m_nWritePos, buf, count); m_nWritePos += count; m_bFull = (m_nWritePos == m_nReadPos); return count; } else { memcpy(m_pBuf + m_nWritePos, buf, leftcount); m_nWritePos = (m_nReadPos > count - leftcount) ? count - leftcount : m_nWritePos; memcpy(m_pBuf, buf + leftcount, m_nWritePos); m_bFull = (m_nWritePos == m_nReadPos); return leftcount + m_nWritePos; } } else if(m_nReadPos < m_nWritePos) // 有剩余空间可写入 { /* == 内存模型 == (empty) (data) (empty) |-------------------|----------------------------|---------------------------| m_nReadPos m_nWritePos (leftcount) */ // 剩余缓冲区大小(从写入位置到缓冲区尾) int leftcount = m_nBufSize - m_nWritePos; if(leftcount > count) // 有足够的剩余空间存放 { memcpy(m_pBuf + m_nWritePos, buf, count); m_nWritePos += count; m_bFull = (m_nReadPos == m_nWritePos); assert(m_nReadPos <= m_nBufSize); assert(m_nWritePos <= m_nBufSize); return count; } else // 剩余空间不足 { // 先填充满剩余空间,再回头找空间存放 memcpy(m_pBuf + m_nWritePos, buf, leftcount); m_nWritePos = (m_nReadPos >= count - leftcount) ? count - leftcount : m_nReadPos; memcpy(m_pBuf, buf + leftcount, m_nWritePos); m_bFull = (m_nReadPos == m_nWritePos); assert(m_nReadPos <= m_nBufSize); assert(m_nWritePos <= m_nBufSize); return leftcount + m_nWritePos; } } else { /* == 内存模型 == (unread) (read) (unread) |-------------------|----------------------------|---------------------------| m_nWritePos (leftcount) m_nReadPos */ int leftcount = m_nReadPos - m_nWritePos; if(leftcount > count) { // 有足够的剩余空间存放 memcpy(m_pBuf + m_nWritePos, buf, count); m_nWritePos += count; m_bFull = (m_nReadPos == m_nWritePos); assert(m_nReadPos <= m_nBufSize); assert(m_nWritePos <= m_nBufSize); return count; } else { // 剩余空间不足时要丢弃后面的数据 memcpy(m_pBuf + m_nWritePos, buf, leftcount); m_nWritePos += leftcount; m_bFull = (m_nReadPos == m_nWritePos); assert(m_bFull); assert(m_nReadPos <= m_nBufSize); assert(m_nWritePos <= m_nBufSize); return leftcount; } }}/************************************************************************//* 从缓冲区读数据,返回实际读取的字节数 *//************************************************************************/int CCycleBuffer::Read(char* buf, int count){ if(count <= 0) return 0; m_bFull = false; if(m_bEmpty) // 缓冲区空,不能继续读取数据 { return 0; } else if(m_nReadPos == m_nWritePos) // 缓冲区满时 { /* == 内存模型 == (data) m_nReadPos (data) |--------------------------------|--------------------------------------------| m_nWritePos m_nBufSize */ int leftcount = m_nBufSize - m_nReadPos; if(leftcount > count) { memcpy(buf, m_pBuf + m_nReadPos, count); m_nReadPos += count; m_bEmpty = (m_nReadPos == m_nWritePos); return count; } else { memcpy(buf, m_pBuf + m_nReadPos, leftcount); m_nReadPos = (m_nWritePos > count - leftcount) ? count - leftcount : m_nWritePos; memcpy(buf + leftcount, m_pBuf, m_nReadPos); m_bEmpty = (m_nReadPos == m_nWritePos); return leftcount + m_nReadPos; } } else if(m_nReadPos < m_nWritePos) // 写指针在前(未读数据是连接的) { /* == 内存模型 == (read) (unread) (read) |-------------------|----------------------------|---------------------------| m_nReadPos m_nWritePos m_nBufSize */ int leftcount = m_nWritePos - m_nReadPos; int c = (leftcount > count) ? count : leftcount; memcpy(buf, m_pBuf + m_nReadPos, c); m_nReadPos += c; m_bEmpty = (m_nReadPos == m_nWritePos); assert(m_nReadPos <= m_nBufSize); assert(m_nWritePos <= m_nBufSize); return c; } else // 读指针在前(未读数据可能是不连接的) { /* == 内存模型 == (unread) (read) (unread) |-------------------|----------------------------|---------------------------| m_nWritePos m_nReadPos m_nBufSize */ int leftcount = m_nBufSize - m_nReadPos; if(leftcount > count) // 未读缓冲区够大,直接读取数据 { memcpy(buf, m_pBuf + m_nReadPos, count); m_nReadPos += count; m_bEmpty = (m_nReadPos == m_nWritePos); assert(m_nReadPos <= m_nBufSize); assert(m_nWritePos <= m_nBufSize); return count; } else // 未读缓冲区不足,需回到缓冲区头开始读 { memcpy(buf, m_pBuf + m_nReadPos, leftcount); m_nReadPos = (m_nWritePos >= count - leftcount) ? count - leftcount : m_nWritePos; memcpy(buf, m_pBuf, m_nReadPos); m_bEmpty = (m_nReadPos == m_nWritePos); assert(m_nReadPos <= m_nBufSize); assert(m_nWritePos <= m_nBufSize); return leftcount + m_nReadPos; } } } /************************************************************************/ /* 获取缓冲区有效数据长度 */ /************************************************************************/ int CCycleBuffer::GetLength() { if(m_bEmpty) { return 0; } else if(m_bFull) { return m_nBufSize; } else if(m_nReadPos < m_nWritePos) { return m_nWritePos - m_nReadPos; } else { return m_nBufSize - m_nReadPos + m_nWritePos; } } void CCycleBuffer::Empty() { m_nReadPos = 0; m_nWritePos = 0; m_bEmpty = true; m_bFull = false;} bool CCycleBuffer::isEmpty(){ return m_bEmpty; } bool CCycleBuffer::isFull(){ return m_bFull; }
0 0
- C++播放RTMP源代码-RTMPClient C++
- C#+音乐播放器+源代码+微软解码技术
- linux源代码-fork.c源代码
- C#winfrom播放音乐
- [C#] DirectX 播放声音
- C#Media播放器
- C语言播放视频
- C语言播放音乐
- C#-播放器相关
- c语言播放音乐
- c编写播放器
- C语言病毒源代码
- RTree.c 最新源代码
- C语言库函数源代码
- vsprintf.c源代码
- C语言小游戏源代码
- c/c++源代码
- test_irq.c 源代码 读书笔记
- 二叉树的深度
- linux的自动任务创建
- *(p+i) ,C语言数组指针_C语言中文网
- POJ 3617 Best Cow Line - 贪心
- web开发中相对路径
- C++播放RTMP源代码-RTMPClient C++
- Matlab中image、imagesc和imshow函数用法解析
- King's Cake
- 【色彩摸鱼】彩虹色的字体。。
- MySql优化【5】--合理的硬件资源和操作系统
- 深入理解SystemServer
- HDU 1800 字符Hash
- 深度理解链式前向星
- Rxjava、retrofit初探