开发环境:
WINDOWS7 32bit
MINGW
eclipse juno cdt
1、首先你要编译好FFMPEG,
a) 方法一:可以去
官网
下载源码,用MINGW编译(编译时记得支持H264,当然,事先得下载并编译好libx264,
视频技术论坛
里有很多介绍)
b) 方法二:更加省心省力的方法是,下载别人已经编译好的资源,如
ZeranoeFFmpeg
的,下载他的dev版本,包含了头文件,链接库等必须的东西,当然,这东西已经是支持H264的了。
2、以下的就是代码部分了:
a) 先声明必要的变量:
AVFormatContext *fmtctx; AVStream *video_st; AVCodec *video_codec; const int FPS = 25; /* 25 images/s */ const char *RDIP = “127.0.0.1”; unsigned int RDPORT = 5678; const unsigned int OUTWIDTH = 720; const unsigned int OUTHEIGHT = 480; av_register_all(); avformat_network_init();
b) 初始化AV容器
fmtctx = avformat_alloc_context();
c) 获得输出格式,这里是RTP网络流
fmtctx->oformat = av_guess_format("rtp", NULL, NULL);
d)打开网络流
snprintf(fmtctx->filename, sizeof(fmtctx->filename),"rtp://%s:%d",RDIP,RDPORT); avio_open(&fmtctx->pb,fmtctx->filename, AVIO_FLAG_WRITE)
e) 开始添加H264视频流
video_st = NULL;video_st = add_video_stream(fmtctx, &video_codec, AV_CODEC_ID_H264);
其中,add_video_stream函数为:
add_video_stream(AVFormatContext *oc,AVCodec **codec, enum AVCodecID codec_id) { AVCodecContext *c; AVStream *st; /* find the video encoder */ *codec = avcodec_find_encoder(codec_id); st = avformat_new_stream(oc, *codec); c = st->codec; avcodec_get_context_defaults3(c, *codec); c->codec_id = codec_id; c->width = OUTWIDTH; c->height = OUTHEIGHT; c->time_base.den = FPS; c->time_base.num = 1; c->pix_fmt = PIX_FMT_YUV420P; if(oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags|= CODEC_FLAG_GLOBAL_HEADER; av_opt_set(c->priv_data, "preset", "ultrafast", 0); av_opt_set(c->priv_data, "tune","stillimage,fastdecode,zerolatency",0); av_opt_set(c->priv_data, "x264opts","crf=26:vbv-maxrate=728:vbv-bufsize=364:keyint=25",0);return st;} // OPEN THE CODE avcodec_open2(video_st->codec, video_codec, NULL); /* Write the stream header, if any. */ avformat_write_header(fmtctx, NULL);
f) 现在,就可以不断的编码数据,并发生数据了
AVFrame* m_pYUVFrame = avcodec_alloc_frame(); while(1) //这里设置成无限循环,你可以设置成250,或其他数进行测试,观看结果 { fill_yuv_image(m_pYUVFrame, video_st->codec->frame_number,OUTWIDTH, OUTHEIGHT); /* encode the image */ AVPacket pkt; int got_output = 0; av_init_packet(&pkt); pkt.data = NULL; // packet data will be allocated by the encoder pkt.size = 0; pkt.pts = AV_NOPTS_VALUE; pkt.dts =AV_NOPTS_VALUE; m_pYUVFrame->pts = video_st->codec->frame_number; ret = avcodec_encode_video2(c, &pkt,frame, &got_output); if (ret < 0) {fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret)); exit(1); } /* If size is zero, it means the image was buffered. */ if (got_output) { if (c->coded_frame->key_frame)pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = st->index; if (pkt.pts != AV_NOPTS_VALUE ) { pkt.pts = av_rescale_q(pkt.pts,video_st->codec->time_base, video_st->time_base); } if(pkt.dts !=AV_NOPTS_VALUE ) { pkt.dts = av_rescale_q(pkt.dts,video_st->codec->time_base, video_st->time_base); } /* Write the compressed frame to the media file. */ ret = av_interleaved_write_frame(oc,&pkt); } else { ret = 0; } }
g) Fill_yuv_image函数:
/* Prepare a dummy image. */ static void fill_yuv_image(AVPicture *pict,int frame_index,int width, int height) { int x, y, i; i = frame_index; /* Y */ for (y = 0; y < height; y++) for (x = 0; x < width; x++) pict->data[0][y * pict->linesize[0] +x] = x + y + i * 3; /* Cb and Cr */ for (y = 0; y < height / 2; y++) { for (x = 0; x < width / 2; x++) { pict->data[1][y * pict->linesize[1] +x] = 128 + y + i * 2; pict->data[2][y * pict->linesize[2] +x] = 64 + x + i * 5; } } }
h) 打印sdp信息,仅需一次,打印的sdp信息,用在VLC播放器结束网络视频流时用到
//打印sdp信息 char sdp[2048]; av_sdp_create(&fmtctx,1, sdp, sizeof(sdp)); printf("%s\n",sdp); fflush(stdout);
i)最后,做一些清理工作
avcodec_free_frame(&m_pYUVFrame); av_write_trailer(fmtctx); /* Free the streams. */ for (unsigned int i = 0; i< fmtctx->nb_streams;i++) { av_freep(&fmtctx->streams->codec); av_freep(&fmtctx->streams); } if(!(fmtctx->oformat->flags& AVFMT_NOFILE)) /* Close the output file. */ avio_close(fmtctx->pb); /*free the stream */ av_free(fmtctx);
3、编译代码,记得添加库文件,运行一次代码,不用死循环,设置不用循环,因为是要让他打印出sdp文件的信息。得到sdp信息,比如我精简成如下:
c=IN IP4 127.0.0.1 m=video 56782 RTP/AVP 96 a=rtpmap:96 H264/90000 a=framerate:25 a=fmtp:96 packetization-mode=1
把这些信息保存到一个文本文件,并改名为sdp后缀,如mySDP.sdp。
4、从
官网
下载VLC播放器,重新运行上述的代码,这一次要循环,具体循环多久,你自己决定,这一次是正式测试了。代码跑起来后,把刚刚的sdp文件用VLC打开,直接把sdp文件拖到VLC播放器中就行了。等待缓冲,就可以看到效果了。
5、代码中剩掉了出错检查部分,请自行添加。
6、关于IP地址,这里是127.0.0.1,是供本机测试,可以改成制定的接受数据的电脑IP地址,或者广播地址IP地址。
7、经本人测试,局域网内不同电脑间测试,刚开始播放良好,进过一段时间后,开始出现吊针现象。经多次修改av_opt_set里面的参数(是修改x264的参数配置)也无法达到较理想的想过。如果有人可以做到长时间稳定播放,望指教。
转载自:http://bbs.chinavideo.org/forum.php?mod=viewthread&tid=16186