FFMPEG教程3_播放声音(使用2014年新SDK重新整理编译通过)
2014-10-01 07:56
756 查看
ffmpeg 教程3,播放声音,SDL2.0,FFmpeg 2.4
龚希鹏 2014.10.1 章原创,转载请注明出处
龚希鹏 2014.10.1 章原创,转载请注明出处
#include "stdafx.h" #include "stdafx.h" #include <Windows.h> #pragma warning( disable : 4312 ) #pragma warning( disable : 4244 ) #pragma warning( disable : 4311 ) #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/avutil.h> #include <libswscale/swscale.h> #include <SDL/SDL.h> #include <SDL/SDL_video.h> // SDL #include <SDL/SDL_thread.h> // SDL #ifdef __cplusplus } #endif #pragma comment( lib, "libgcc.a") #pragma comment( lib, "libmingwex.a") #pragma comment( lib, "libcoldname.a") #pragma comment( lib, "libavcodec.a") #pragma comment( lib, "libavformat.a") #pragma comment( lib, "libavutil.a") #pragma comment( lib, "libswscale.a") #pragma comment( lib, "libz.a") #pragma comment( lib, "libfaac.a") #pragma comment( lib, "libgsm.a") #pragma comment( lib, "libmp3lame.a") #pragma comment( lib, "libogg.a") #pragma comment( lib, "libspeex.a") #pragma comment( lib, "libtheora.a") #pragma comment( lib, "libvorbis.a") #pragma comment( lib, "libvorbisenc.a") #pragma comment( lib, "libx264.a") #pragma comment( lib, "xvidcore.a") #pragma comment( lib, "wsock32.lib") #pragma comment( lib, "vfw32.lib") #pragma comment( lib, "sdl2main.lib") #pragma comment( lib, "sdl2.lib") #pragma comment(lib, "winmm.lib ") #pragma comment(lib,"Version.lib") #pragma comment(lib,"imm32.lib") #ifdef __MINGW32__ #undef main /* Prevents SDL from overriding main() */ #endif #define SDL_AUDIO_BUFFER_SIZE 1024 typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; int nb_packets; int size; SDL_mutex *mutex; SDL_cond *cond; } PacketQueue; PacketQueue audioq; int quit = 0; void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; if(av_dup_packet(pkt) < 0) { return -1; } pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList)); if (!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL; 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; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0; } static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { AVPacketList *pkt1; int ret; SDL_LockMutex(q->mutex); for(;;) { if(quit) { 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; *pkt = pkt1->pkt; av_free(pkt1); ret = 1; break; } else if (!block) { ret = 0; break; } else { SDL_CondWait(q->cond, q->mutex); } } SDL_UnlockMutex(q->mutex); return ret; } int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) { static AVPacket pkt; static uint8_t *audio_pkt_data = NULL; static int audio_pkt_size = 0; int len1, data_size; for(;;) { while(audio_pkt_size > 0) { data_size = buf_size; len1 = avcodec_decode_audio3(aCodecCtx, (int16_t *)audio_buf, &data_size, &pkt); //原来这个函数已经过时,新的ffpeg应当使用avcodec_decode_audio3 //len1 = avcodec_decode_audio2(aCodecCtx, (int16_t *)audio_buf, &data_size, audio_pkt_data, audio_pkt_size); if(len1 < 0) { /* if error, skip frame */ audio_pkt_size = 0; break; } audio_pkt_data += len1; audio_pkt_size -= len1; if(data_size <= 0) { /* No data yet, get more frames */ continue; } /* We have data, return it and come back for more later */ return data_size; } if(pkt.data) av_free_packet(&pkt); if(quit) { return -1; } if(packet_queue_get(&audioq, &pkt, 1) < 0) { return -1; } audio_pkt_data = pkt.data; audio_pkt_size = pkt.size; } } void audio_callback(void *userdata, Uint8 *stream, int len) { AVCodecContext *aCodecCtx = (AVCodecContext *)userdata; int len1, audio_size; static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; static unsigned int audio_buf_size = 0; static unsigned int audio_buf_index = 0; //这个为SDL2.0增加 ,gongxp 2014注释 SDL_memset(stream, 0, len); while(len > 0) { if(audio_buf_index >= audio_buf_size) { /* We have already sent all our data; get more */ audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf)); if(audio_size < 0) { /* If error, output silence */ audio_buf_size = 1024; // arbitrary? memset(audio_buf, 0, audio_buf_size); } else { audio_buf_size = audio_size; } audio_buf_index = 0; } len1 = audio_buf_size - audio_buf_index; if(len1 > len) len1 = len; memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1); len -= len1; stream += len1; audio_buf_index += len1; } } int _tmain(int argc, _TCHAR* argv[]) { AVFormatContext *pFormatCtx = NULL; int i, videoStream, audioStream,iret; AVCodecContext *pCodecCtx = NULL; AVCodec *pCodec = NULL; AVFrame *pFrame = NULL; AVFrame *pFrameYUV = NULL; AVPacket packet; int frameFinished; //float aspect_ratio; int numBytes; uint8_t *buffer; struct SwsContext *sws_ctx = NULL; AVCodecContext *aCodecCtx = NULL; AVCodec *aCodec = NULL; SDL_Texture *bmp = NULL; SDL_Window *screen = NULL; SDL_Rect rect; SDL_Event event; SDL_AudioSpec wanted_spec, spec; if(argc < 2) { fprintf(stderr, "Usage: test <file>\n"); exit(1); } av_register_all(); if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); exit(1); } //打开视频 if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)return -1; // 打开视频文件失败 if(av_find_stream_info(pFormatCtx)<0)return -1; // Couldn't find stream information //打印标准的转储信息 av_dump_format(pFormatCtx, 0, argv[1], 0); videoStream=-1; audioStream=-1; for(i=0; i<(int)pFormatCtx->nb_streams; i++) { if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && videoStream < 0) { videoStream=i; } if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&audioStream < 0) { audioStream=i; } } if(videoStream==-1)return -1; if(audioStream==-1)return -1; aCodecCtx=pFormatCtx->streams[audioStream]->codec; // 设置音频设置编码信息 wanted_spec.freq = aCodecCtx->sample_rate; wanted_spec.format = AUDIO_S16SYS; wanted_spec.channels = aCodecCtx->channels; wanted_spec.silence = 0; wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; wanted_spec.callback = audio_callback; wanted_spec.userdata = aCodecCtx; if(SDL_OpenAudio(&wanted_spec, &spec) < 0) { fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); return -1; } aCodec = avcodec_find_decoder(aCodecCtx->codec_id); if(!aCodec) { fprintf(stderr, "Unsupported codec!\n"); return -1; } avcodec_open(aCodecCtx, aCodec); // audio_st = pFormatCtx->streams[index] packet_queue_init(&audioq); SDL_PauseAudio(0); // Get a pointer to the codec context for the video stream pCodecCtx=pFormatCtx->streams[videoStream]->codec; // Find the decoder for the video stream pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL) { fprintf(stderr, "Unsupported codec!\n"); return -1; // Codec not found } // Open codec if(avcodec_open(pCodecCtx, pCodec)<0) return -1; // Could not open codec // Allocate video frame pFrame=avcodec_alloc_frame(); pFrameYUV = avcodec_alloc_frame(); if(pFrameYUV==NULL)return -1; int screen_w = pCodecCtx->width; int screen_h = pCodecCtx->height; screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h,SDL_WINDOW_OPENGL); //screen = SDL_CreateWindow("My Game Window",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,pCodecCtx->width, pCodecCtx->height,SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL); SDL_Renderer *renderer = SDL_CreateRenderer(screen, -1, 0); if(!screen) { fprintf(stderr, "SDL: could not set video mode - exiting\n"); exit(1); } //bmp = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_YV12,SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width,pCodecCtx->height);//如果使用这个SDL_PIXELFORMAT_YV12,显示颜色失真! bmp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width,pCodecCtx->height); //用于保存显示到SDL sws_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height, pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,PIX_FMT_YUV420P,SWS_BILINEAR,NULL,NULL,NULL); ////确定所需的缓冲区大小和分配缓冲区,用于显示到SDL的 numBytes = avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width,pCodecCtx->height); buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); // Assign appropriate parts of buffer to image planes in pFrameRGB // Note that pFrameRGB is an AVFrame, but AVFrame is a superset of AVPicture //分配适当的部分缓冲给在pFrameRGB中的图像平面,请注意,pFrameRGB 是一个AVFrame,但AVFrame却是AVPicture的超集,用于显示到SDL avpicture_fill((AVPicture *)pFrameYUV, buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); i=0; rect.x = 0; rect.y = 0; rect.w = pCodecCtx->width; rect.h = pCodecCtx->height; while(av_read_frame(pFormatCtx, &packet)>=0) { // Is this a packet from the video stream? if(packet.stream_index==videoStream) { // Decode video frame iret = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,&packet); if(iret < 0){ printf("Decode Error.(解码错误)\n"); return -1; } // 成功获得了视频帧? if(frameFinished) { // 将图像从原生格式转换成为目标格式 sws_scale(sws_ctx,(uint8_t const * const *)pFrame->data,pFrame->linesize, 0,pCodecCtx->height, pFrameYUV->data,pFrameYUV->linesize); //送到SDL2.0中显示,和SDL1.0明显不同! SDL_UpdateTexture( bmp, &rect, pFrameYUV->data[0], pFrameYUV->linesize[0] ); SDL_RenderClear( renderer ); SDL_RenderCopy( renderer, bmp, &rect, &rect ); SDL_RenderPresent( renderer ); } } else if(packet.stream_index==audioStream) { packet_queue_put(&audioq, &packet); } else { av_free_packet(&packet); } // Free the packet that was allocated by av_read_frame SDL_PollEvent(&event); switch(event.type) { case SDL_QUIT: quit = 1; SDL_Quit(); exit(0); break; default: break; } } // Free the YUV frame av_free(pFrame); // Close the codec avcodec_close(pCodecCtx); // Close the video file av_close_input_file(pFormatCtx); return 0; }
相关文章推荐
- FFMPEG教程4_让程序模块化-增加多线程(使用2014年新SDK重新整理编译通过)
- FFMPEG教程5_同步视频(使用2014年新SDK重新整理编译通过)
- FFMPEG教程6_同步音频(使用2014年新SDK重新整理编译通过)
- FFMPEG教程1_解码后保存图片(使用2014年新SDK重新整理编译通过)
- FFMPEG教程7_快进和快退(使用2014年新SDK重新整理编译通过)
- FFMPEG教程2_解码后在屏幕显示(使用2014年新SDK重新整理编译通过)
- 天草VIP_逆向分析视频教程(无KEY高清版)[2014年重新整理可在线播放版]
- iOS编译FFmpeg及使用实现视频播放
- 使用C#实现通过网络发送和播放麦克风的声音
- ffmpeg和sdl教程(三) --- 播放声音
- 在Ogre中使用DirectShow来播放视频--重新封装并测试通过
- 通过windows + MinGW + MSYS编译ffmpeg实现文件播放
- ffmpeg 和 SDL的教程 tutorial3学习--播放声音
- FFmpeg和SDL教程(三)播放声音
- FFmpeg和SDL教程(三)播放声音
- ffmpeg的编译(for x86,for arm)安装及使用(网络资料整理)
- ffmpeg的编译(for x86,for arm)安装及使用(网络资料整理)
- KITL 使用教程(重新整理)
- 在Android中通过jni方式使用编译好的FFmpeg库-Android中使用FFmpeg媒体库(二)
- 在Android中通过jni方式使用编译好的FFmpeg库-Android中使用FFmpeg媒体库(二)