ffmpeg实现录屏+录音
2015-06-30 18:13
537 查看
1、概述
最简单的基于FFmpeg的***Device例子(屏幕录制+声音采集),视频采用mpeg4编码,音频采用aac编码,并生成mp4文件,其中fifo是此程序的关键,此程序只是一个demo很多优化都没做,仅供参考。2、代码
<pre name="code" class="cpp">/** *最简单的基于FFmpeg的***Device例子(屏幕录制+声音采集),视频采用mpeg4编码,音频采用aac编码,并生成mp4文件 *缪国凯 MK *821486004@qq.com */ #include "stdafx.h" #ifdef __cplusplus extern "C" { #endif #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavdevice/avdevice.h" #include "libavutil/audio_fifo.h" #pragma comment(lib, "avcodec.lib") #pragma comment(lib, "avformat.lib") #pragma comment(lib, "avutil.lib") #pragma comment(lib, "avdevice.lib") #pragma comment(lib, "avfilter.lib") //#pragma comment(lib, "avfilter.lib") //#pragma comment(lib, "postproc.lib") //#pragma comment(lib, "swresample.lib") #pragma comment(lib, "swscale.lib") #ifdef __cplusplus }; #endif ***FormatContext *pFormatCtx_Video = NULL, *pFormatCtx_Audio = NULL, *pFormatCtx_Out = NULL; ***CodecContext *pCodecCtx_Video; ***Codec *pCodec_Video; ***FifoBuffer *fifo_video = NULL; ***AudioFifo *fifo_audio = NULL; int VideoIndex, AudioIndex; CRITICAL_SECTION AudioSection, VideoSection; SwsContext *img_convert_ctx; int frame_size = 0; uint8_t *picture_buf = NULL, *frame_buf = NULL; bool bCap = true; DWORD WINAPI ScreenCapThreadProc( LPVOID lpParam ); DWORD WINAPI AudioCapThreadProc( LPVOID lpParam ); int OpenVideoCapture() { ***InputFormat *ifmt=av_find_input_format("gdigrab"); //这里可以加参数打开,例如可以指定采集帧率 ***Dictionary *options = NULL; av_dict_set(&options, "framerate", "15", NULL); //av_dict_set(&options,"offset_x","20",0); //The distance from the top edge of the screen or desktop //av_dict_set(&options,"offset_y","40",0); //Video frame size. The default is to capture the full screen //av_dict_set(&options,"video_size","320x240",0); if(avformat_open_input(&pFormatCtx_Video, "desktop", ifmt, &options)!=0) { printf("Couldn't open input stream.(无法打开视频输入流)\n"); return -1; } if(avformat_find_stream_info(pFormatCtx_Video,NULL)<0) { printf("Couldn't find stream information.(无法获取视频流信息)\n"); return -1; } if (pFormatCtx_Video->streams[0]->codec->codec_type != ***MEDIA_TYPE_VIDEO) { printf("Couldn't find video stream information.(无法获取视频流信息)\n"); return -1; } pCodecCtx_Video = pFormatCtx_Video->streams[0]->codec; pCodec_Video = avcodec_find_decoder(pCodecCtx_Video->codec_id); if(pCodec_Video == NULL) { printf("Codec not found.(没有找到解码器)\n"); return -1; } if(avcodec_open2(pCodecCtx_Video, pCodec_Video, NULL) < 0) { printf("Could not open codec.(无法打开解码器)\n"); return -1; } img_convert_ctx = sws_getContext(pCodecCtx_Video->width, pCodecCtx_Video->height, pCodecCtx_Video->pix_fmt, pCodecCtx_Video->width, pCodecCtx_Video->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); frame_size = avpicture_get_size(pCodecCtx_Video->pix_fmt, pCodecCtx_Video->width, pCodecCtx_Video->height); //申请30帧缓存 fifo_video = av_fifo_alloc(30 * avpicture_get_size(***_PIX_FMT_YUV420P, pCodecCtx_Video->width, pCodecCtx_Video->height)); return 0; } static char *dup_wchar_to_utf8(wchar_t *w) { char *s = NULL; int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0); s = (char *) av_malloc(l); if (s) WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0); return s; } int OpenAudioCapture() { //查找输入方式 ***InputFormat *pAudioInputFmt = av_find_input_format("dshow"); //以Direct Show的方式打开设备,并将 输入方式 关联到格式上下文 char * psDevName = dup_wchar_to_utf8(L"audio=麦克风 (Realtek High Definition Au"); if (avformat_open_input(&pFormatCtx_Audio, psDevName, pAudioInputFmt,NULL) < 0) { printf("Couldn't open input stream.(无法打开音频输入流)\n"); return -1; } if(avformat_find_stream_info(pFormatCtx_Audio,NULL)<0) return -1; if(pFormatCtx_Audio->streams[0]->codec->codec_type != ***MEDIA_TYPE_AUDIO) { printf("Couldn't find video stream information.(无法获取音频流信息)\n"); return -1; } ***Codec *tmpCodec = avcodec_find_decoder(pFormatCtx_Audio->streams[0]->codec->codec_id); if(0 > avcodec_open2(pFormatCtx_Audio->streams[0]->codec, tmpCodec, NULL)) { printf("can not find or open audio decoder!\n"); } return 0; } int OpenOutPut() { ***Stream *pVideoStream = NULL, *pAudioStream = NULL; const char *outFileName = "test.mp4"; avformat_alloc_output_context2(&pFormatCtx_Out, NULL, NULL, outFileName); if (pFormatCtx_Video->streams[0]->codec->codec_type == ***MEDIA_TYPE_VIDEO) { ***CodecContext *videoCodecCtx; VideoIndex = 0; pVideoStream = avformat_new_stream(pFormatCtx_Out, NULL); if (!pVideoStream) { printf("can not new stream for output!\n"); return -1; } //set codec context param pVideoStream->codec->codec = avcodec_find_encoder(***_CODEC_ID_MPEG4); pVideoStream->codec->height = pFormatCtx_Video->streams[0]->codec->height; pVideoStream->codec->width = pFormatCtx_Video->streams[0]->codec->width; pVideoStream->codec->time_base = pFormatCtx_Video->streams[0]->codec->time_base; pVideoStream->codec->sample_aspect_ratio = pFormatCtx_Video->streams[0]->codec->sample_aspect_ratio; // take first format from list of supported formats pVideoStream->codec->pix_fmt = pFormatCtx_Out->streams[VideoIndex]->codec->codec->pix_fmts[0]; //open encoder if (!pVideoStream->codec->codec) { printf("can not find the encoder!\n"); return -1; } if (pFormatCtx_Out->oformat->flags & ***FMT_GLOBALHEADER) pVideoStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; if ((avcodec_open2(pVideoStream->codec, pVideoStream->codec->codec, NULL)) < 0) { printf("can not open the encoder\n"); return -1; } } if(pFormatCtx_Audio->streams[0]->codec->codec_type == ***MEDIA_TYPE_AUDIO) { ***CodecContext *pOutputCodecCtx; AudioIndex = 1; pAudioStream = avformat_new_stream(pFormatCtx_Out, NULL); pAudioStream->codec->codec = avcodec_find_encoder(pFormatCtx_Out->oformat->audio_codec); pOutputCodecCtx = pAudioStream->codec; pOutputCodecCtx->sample_rate = pFormatCtx_Audio->streams[0]->codec->sample_rate; pOutputCodecCtx->channel_layout = pFormatCtx_Out->streams[0]->codec->channel_layout; pOutputCodecCtx->channels = av_get_channel_layout_nb_channels(pAudioStream->codec->channel_layout); if(pOutputCodecCtx->channel_layout == 0) { pOutputCodecCtx->channel_layout = ***_CH_LAYOUT_STEREO; pOutputCodecCtx->channels = av_get_channel_layout_nb_channels(pOutputCodecCtx->channel_layout); } pOutputCodecCtx->sample_fmt = pAudioStream->codec->codec->sample_fmts[0]; ***Rational time_base={1, pAudioStream->codec->sample_rate}; pAudioStream->time_base = time_base; //audioCodecCtx->time_base = time_base; pOutputCodecCtx->codec_tag = 0; if (pFormatCtx_Out->oformat->flags & ***FMT_GLOBALHEADER) pOutputCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; if (avcodec_open2(pOutputCodecCtx, pOutputCodecCtx->codec, 0) < 0) { //编码器打开失败,退出程序 return -1; } } if (!(pFormatCtx_Out->oformat->flags & ***FMT_NOFILE)) { if(avio_open(&pFormatCtx_Out->pb, outFileName, ***IO_FLAG_WRITE) < 0) { printf("can not open output file handle!\n"); return -1; } } if(avformat_write_header(pFormatCtx_Out, NULL) < 0) { printf("can not write the header of the output file!\n"); return -1; } return 0; } int _tmain(int argc, _TCHAR* argv[]) { av_register_all(); avdevice_register_all(); if (OpenVideoCapture() < 0) { return -1; } if (OpenAudioCapture() < 0) { return -1; } if (OpenOutPut() < 0) { return -1; } InitializeCriticalSection(&VideoSection); InitializeCriticalSection(&AudioSection); ***Frame *picture = av_frame_alloc(); int size = avpicture_get_size(pFormatCtx_Out->streams[VideoIndex]->codec->pix_fmt, pFormatCtx_Out->streams[VideoIndex]->codec->width, pFormatCtx_Out->streams[VideoIndex]->codec->height); picture_buf = new uint8_t[size]; avpicture_fill((***Picture *)picture, picture_buf, pFormatCtx_Out->streams[VideoIndex]->codec->pix_fmt, pFormatCtx_Out->streams[VideoIndex]->codec->width, pFormatCtx_Out->streams[VideoIndex]->codec->height); //star cap screen thread CreateThread( NULL, 0, ScreenCapThreadProc, 0, 0, NULL); //star cap audio thread CreateThread( NULL, 0, AudioCapThreadProc, 0, 0, NULL); int64_t cur_pts_v=0,cur_pts_a=0; int VideoFrameIndex = 0, AudioFrameIndex = 0; while(1) { if (_kbhit() != 0 && bCap) { bCap = false; Sleep(2000);//简单的用sleep等待采集线程关闭 } if (fifo_audio && fifo_video) { int sizeAudio = av_audio_fifo_size(fifo_audio); int sizeVideo = av_fifo_size(fifo_video); //缓存数据写完就结束循环 if (av_audio_fifo_size(fifo_audio) <= pFormatCtx_Out->streams[AudioIndex]->codec->frame_size && av_fifo_size(fifo_video) <= frame_size && !bCap) { break; } } if(av_compare_ts(cur_pts_v, pFormatCtx_Out->streams[VideoIndex]->time_base, cur_pts_a,pFormatCtx_Out->streams[AudioIndex]->time_base) <= 0) { //read data from fifo if (av_fifo_size(fifo_video) < frame_size && !bCap) { cur_pts_v = 0x7fffffffffffffff; } if(av_fifo_size(fifo_video) >= size) { EnterCriticalSection(&VideoSection); av_fifo_generic_read(fifo_video, picture_buf, size, NULL); LeaveCriticalSection(&VideoSection); avpicture_fill((***Picture *)picture, picture_buf, pFormatCtx_Out->streams[VideoIndex]->codec->pix_fmt, pFormatCtx_Out->streams[VideoIndex]->codec->width, pFormatCtx_Out->streams[VideoIndex]->codec->height); //pts = n * ((1 / timbase)/ fps); picture->pts = VideoFrameIndex * ((pFormatCtx_Video->streams[0]->time_base.den / pFormatCtx_Video->streams[0]->time_base.num) / 15); int got_picture = 0; ***Packet pkt; av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; int ret = avcodec_encode_video2(pFormatCtx_Out->streams[VideoIndex]->codec, &pkt, picture, &got_picture); if(ret < 0) { //编码错误,不理会此帧 continue; } if (got_picture==1) { pkt.stream_index = VideoIndex; pkt.pts = av_rescale_q_rnd(pkt.pts, pFormatCtx_Video->streams[0]->time_base, pFormatCtx_Out->streams[VideoIndex]->time_base, (***Rounding)(***_ROUND_NEAR_INF|***_ROUND_PASS_MINMAX)); pkt.dts = av_rescale_q_rnd(pkt.dts, pFormatCtx_Video->streams[0]->time_base, pFormatCtx_Out->streams[VideoIndex]->time_base, (***Rounding)(***_ROUND_NEAR_INF|***_ROUND_PASS_MINMAX)); pkt.duration = ((pFormatCtx_Out->streams[0]->time_base.den / pFormatCtx_Out->streams[0]->time_base.num) / 15); cur_pts_v = pkt.pts; ret = av_interleaved_write_frame(pFormatCtx_Out, &pkt); //delete[] pkt.data; av_free_packet(&pkt); } VideoFrameIndex++; } } else { if (NULL == fifo_audio) { continue;//还未初始化fifo } if (av_audio_fifo_size(fifo_audio) < pFormatCtx_Out->streams[AudioIndex]->codec->frame_size && !bCap) { cur_pts_a = 0x7fffffffffffffff; } if(av_audio_fifo_size(fifo_audio) >= (pFormatCtx_Out->streams[AudioIndex]->codec->frame_size > 0 ? pFormatCtx_Out->streams[AudioIndex]->codec->frame_size : 1024)) { ***Frame *frame; frame = av_frame_alloc(); frame->nb_samples = pFormatCtx_Out->streams[AudioIndex]->codec->frame_size>0 ? pFormatCtx_Out->streams[AudioIndex]->codec->frame_size: 1024; frame->channel_layout = pFormatCtx_Out->streams[AudioIndex]->codec->channel_layout; frame->format = pFormatCtx_Out->streams[AudioIndex]->codec->sample_fmt; frame->sample_rate = pFormatCtx_Out->streams[AudioIndex]->codec->sample_rate; av_frame_get_buffer(frame, 0); EnterCriticalSection(&AudioSection); av_audio_fifo_read(fifo_audio, (void **)frame->data, (pFormatCtx_Out->streams[AudioIndex]->codec->frame_size > 0 ? pFormatCtx_Out->streams[AudioIndex]->codec->frame_size : 1024)); LeaveCriticalSection(&AudioSection); if (pFormatCtx_Out->streams[0]->codec->sample_fmt != pFormatCtx_Audio->streams[AudioIndex]->codec->sample_fmt || pFormatCtx_Out->streams[0]->codec->channels != pFormatCtx_Audio->streams[AudioIndex]->codec->channels || pFormatCtx_Out->streams[0]->codec->sample_rate != pFormatCtx_Audio->streams[AudioIndex]->codec->sample_rate) { //如果输入和输出的音频格式不一样 需要重采样,这里是一样的就没做 } ***Packet pkt_out; av_init_packet(&pkt_out); int got_picture = -1; pkt_out.data = NULL; pkt_out.size = 0; frame->pts = AudioFrameIndex * pFormatCtx_Out->streams[AudioIndex]->codec->frame_size; if (avcodec_encode_audio2(pFormatCtx_Out->streams[AudioIndex]->codec, &pkt_out, frame, &got_picture) < 0) { printf("can not decoder a frame"); } av_frame_free(&frame); if (got_picture) { pkt_out.stream_index = AudioIndex; pkt_out.pts = AudioFrameIndex * pFormatCtx_Out->streams[AudioIndex]->codec->frame_size; pkt_out.dts = AudioFrameIndex * pFormatCtx_Out->streams[AudioIndex]->codec->frame_size; pkt_out.duration = pFormatCtx_Out->streams[AudioIndex]->codec->frame_size; cur_pts_a = pkt_out.pts; int ret = av_interleaved_write_frame(pFormatCtx_Out, &pkt_out); av_free_packet(&pkt_out); } AudioFrameIndex++; } } } delete[] picture_buf; av_fifo_free(fifo_video); av_audio_fifo_free(fifo_audio); av_write_trailer(pFormatCtx_Out); avio_close(pFormatCtx_Out->pb); avformat_free_context(pFormatCtx_Out); if (pFormatCtx_Video != NULL) { avformat_close_input(&pFormatCtx_Video); pFormatCtx_Video = NULL; } if (pFormatCtx_Audio != NULL) { avformat_close_input(&pFormatCtx_Audio); pFormatCtx_Audio = NULL; } return 0; } DWORD WINAPI ScreenCapThreadProc( LPVOID lpParam ) { ***Packet packet;/* = (***Packet *)av_malloc(sizeof(***Packet))*/; int got_picture; ***Frame *pFrame; pFrame=avcodec_alloc_frame(); ***Frame *picture = avcodec_alloc_frame(); int size = avpicture_get_size(pFormatCtx_Out->streams[VideoIndex]->codec->pix_fmt, pFormatCtx_Out->streams[VideoIndex]->codec->width, pFormatCtx_Out->streams[VideoIndex]->codec->height); //picture_buf = new uint8_t[size]; avpicture_fill((***Picture *)picture, picture_buf, pFormatCtx_Out->streams[VideoIndex]->codec->pix_fmt, pFormatCtx_Out->streams[VideoIndex]->codec->width, pFormatCtx_Out->streams[VideoIndex]->codec->height); FILE *p = NULL; p = fopen("proc_test.yuv", "wb+"); av_init_packet(&packet); int height = pFormatCtx_Out->streams[VideoIndex]->codec->height; int width = pFormatCtx_Out->streams[VideoIndex]->codec->width; int y_size=height*width; while(bCap) { packet.data = NULL; packet.size = 0; if (av_read_frame(pFormatCtx_Video, &packet) < 0) { continue; } if(packet.stream_index == 0) { if (avcodec_decode_video2(pCodecCtx_Video, pFrame, &got_picture, &packet) < 0) { printf("Decode Error.(解码错误)\n"); continue; } if (got_picture) { sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pFormatCtx_Out->streams[VideoIndex]->codec->height, picture->data, picture->linesize); if (av_fifo_space(fifo_video) >= size) { EnterCriticalSection(&VideoSection); av_fifo_generic_write(fifo_video, picture->data[0], y_size, NULL); av_fifo_generic_write(fifo_video, picture->data[1], y_size/4, NULL); av_fifo_generic_write(fifo_video, picture->data[2], y_size/4, NULL); LeaveCriticalSection(&VideoSection); } } } av_free_packet(&packet); //Sleep(50); } av_frame_free(&pFrame); av_frame_free(&picture); //delete[] picture_buf; return 0; } DWORD WINAPI AudioCapThreadProc( LPVOID lpParam ) { ***Packet pkt; ***Frame *frame; frame = av_frame_alloc(); int gotframe; while(bCap) { pkt.data = NULL; pkt.size = 0; if(av_read_frame(pFormatCtx_Audio,&pkt) < 0) { continue; } if (avcodec_decode_audio4(pFormatCtx_Audio->streams[0]->codec, frame, &gotframe, &pkt) < 0) { av_frame_free(&frame); printf("can not decoder a frame"); break; } av_free_packet(&pkt); if (!gotframe) { continue;//没有获取到数据,继续下一次 } if (NULL == fifo_audio) { fifo_audio = av_audio_fifo_alloc(pFormatCtx_Audio->streams[0]->codec->sample_fmt, pFormatCtx_Audio->streams[0]->codec->channels, 30 * frame->nb_samples); } int buf_space = av_audio_fifo_space(fifo_audio); if (av_audio_fifo_space(fifo_audio) >= frame->nb_samples) { EnterCriticalSection(&AudioSection); av_audio_fifo_write(fifo_audio, (void **)frame->data, frame->nb_samples); LeaveCriticalSection(&AudioSection); } } av_frame_free(&frame); return 0; }
注:上面的代码已经过修改,请重新下载工程(或者用代码覆盖以前的版本)。//8-25-15
3、工程地址
http://download.csdn.net/detail/dancing_night/9044441相关文章推荐
- 开源监控软件ganglia安装手册
- A. Case of the Zeros and Ones
- GRBL六:项目笔记
- Android-->巧用XListView,打造万能的下拉刷新控件
- JSP的运行原理及本质
- NGUI 小总结
- UITableView优化
- Windows API timeGetTime Intro
- 欢迎使用CSDN-markdown编辑器
- Java操作PDF之iText超入门
- dubbox消费端和服务端
- 一个模仿布卡那样的划动手势看在线漫画的简单应用DEMO
- 重要通知:博客迁移至 ZYaller
- 判断回文的方法总结
- xml备忘 DTD PCDATA CDATA
- mysql -- 创建存储过程 往数据表中新增字段
- bzoj2584
- 3、javaweb listener session绑定&钝化--监听
- 一种一致性HASH算法的实现方法,附核心代码
- Guice 学习(一)永远的 Hello World