live555源码下载
㈠ live555推送1080p花屏
最近一直研究live555推送rtsp流到easydarwin,实现转发,但是遇到一个问题:live555推送之后的视频流出现花屏,在网上搜罗一大圈之后找到一个答案,就是live555内部OutPacketBuffer默认大小只有60000,即是unsigned OutPacketBuffer::maxSize = 60000;当我推送1080p视频流的时散橡候,用vlc播放,出现部分视频是花的,主要就是缓冲区太小了,将这个值改大一点即可,目前测试改成288000,视频不会出现花屏,问题完美解决。
1、大数据帧花屏
live555推送之后的视频流出现花屏,查看源码DynamicRTSPServer.cpp文件,源码如下:
sms->addSubsession(::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".264") == 0) {
// Assumed to be a H.264 Video Elementary Stream file:
NEW_SMS("H.264 Video");
OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 frames
sms->addSubsession(::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".265") == 0) {
// Assumed to be a H.265 Video Elementary Stream file:
NEW_SMS("H.265 Video");
OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.265 frames
sms->addSubsession(::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".mp3") == 0) {
// Assumed to be a MPEG-1 or 2 Audio file:
NEW_SMS("MPEG-1 or 2 Audio")
查看上面红色部分对于H264和H265输出包最大缓冲100000字节(100K),对于高清视频缓冲区太小了,必需更改大些。目前更改到冲雹旁800000,对于1080P视频使用VLC播放时,不会再出现花屏。
2、循环播放文件
在liveMedia库下的ByteStreamFileSource.cpp文件中的95行,找到
void ByteStreamFileSource::doGetNextFrame() {
if (feof(fFid) || ferror(fFid) || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {
handleClosure();
return;
}
更改为
void ByteStreamFileSource::doGetNextFrame() {
if (feof(fFid) || ferror(fFid) || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {
//handleClosure();
//肆贺return;
fseek(fFid, 0, SEEK_SET);
}
主要思想为,当文件读完后不让关闭文件,而是重新读取文件。经过测试,当VLC关闭RTSP链接后,文件会关闭,重新打开其他文件不受影响。
㈡ 读取实时流时怎样判断解压完一个avpacket
encoding.c
#include<math.h>
#include<libavutil/opt.h>
#include<libavcodec/avcodec.h>
#include<libavutil/imgutils.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<errno.h>
#include<signal.h>
intstop = 0;
constchar *FIFO = "test.264";
/*
* Video encoding example
*/
staticvoid video_encode_example(const char *filename, int codec_id)
{
AVCodec *codec;
AVCodecContext *c= NULL;
int i, ret, x, y, got_output;
FILE *f;
AVFrame *frame;
AVPacket pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
printf("Encode video file %s\n",filename);
/* find the mpeg1 video encoder */
codec = avcodec_find_encoder(codec_id);
if (!codec)
{
fprintf(stderr, "Codec notfound\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c)
{
fprintf(stderr, "Could notallocate video codec context\n");
exit(1);
}
/* put sample parameters */
c->bit_rate = 400000;
/* resolution must be a multiple of two */
c->width = 352;
c->height = 288;
/* frames per second */
c->time_base= (AVRational){1,25};
c->gop_size = 10; /* emit one intraframe every ten frames */
c->max_b_frames=1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if(codec_id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data,"preset", "slow", 0);
/* open it */
if (avcodec_open2(c, codec, NULL) < 0)
{
fprintf(stderr, "Could not opencodec\n");
exit(1);
}
f = fopen(filename, "w");
if (!f)
{
fprintf(stderr, "Could not open%s\n", filename);
exit(1);
}
frame = avcodec_alloc_frame();
if (!frame)
{
fprintf(stderr, "Could notallocate video frame\n");
exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
/* the image can be allocated by any meansand av_image_alloc() is
* just the most convenient way ifav_malloc() is to be used */
ret = av_image_alloc(frame->data,frame->linesize, c->width, c->height, c->pix_fmt, 32);
if (ret < 0)
{
fprintf(stderr, "Could notallocate raw picture buffer\n");
exit(1);
}
/* encode time second of video */
i = 0;
while (!stop)
{
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by theencoder
pkt.size = 0;
fflush(stdout);
/* prepare a mmy image */
/* Y */
for(y=0;y<c->height;y++) {
for(x=0;x<c->width;x++) {
frame->data[0][y *frame->linesize[0] + x] = x + y + i * 3;
}
}
/* Cb and Cr */
for(y=0;y<c->height/2;y++) {
for(x=0;x<c->width/2;x++) {
frame->data[1][y *frame->linesize[1] + x] = 128 + y + i * 2;
frame->data[2][y *frame->linesize[2] + x] = 64 + x + i * 5;
}
}
frame->pts = i;
/* encode the image */
ret = avcodec_encode_video2(c,&pkt, frame, &got_output);
if (ret < 0) {
fprintf(stderr, "Errorencoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n",i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
i++;
}
相关推荐:RTSP服务器实例live555源代码分析
1. RTSP连接的建立过程 RTSPServer类用于构建一个RTSP服务器,该类同时在其内部定义了一个RTSPClientSession类,用于处理单独的客户会话。 首先创建
/* get the delayed frames */
for (got_output = 1; got_output; i++) {
fflush(stdout);
ret = avcodec_encode_video2(c,&pkt, NULL, &got_output);
if (ret < 0) {
fprintf(stderr, "Errorencoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d(size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
/* add sequence end code to have a realmpeg file */
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
avcodec_close(c);
av_free(c);
av_freep(&frame->data[0]);
avcodec_free_frame(&frame);
printf("\n");
}
voidhandle(int sig)
{
if (sig == SIGINT)
stop = 1;
}
intmain(int argc, char **argv)
{
signal(SIGINT, handle);
signal(SIGPIPE, SIG_IGN);
/* register all the codecs */
avcodec_register_all();
if (mkfifo(FIFO, 0644)< 0)
{
if (EEXIST != errno)
{
perror("mkfifo");
exit(1);
}
}
video_encode_example(FIFO,AV_CODEC_ID_H264);
return 0;
}
㈢ live555移植到hi3516做rtsp服务器
live555库本身实现了做rtsp服务器,客户端可以通过rtsp客户端访问服务器上的文件并播放,支持的文件格式如下:
本次任务实现了把live555移植到嵌入式海思芯片hi3516上做rtsp服务器,除了支持客户端播放服务器上上面格式文件外,另添加了实时播放hi3516摄像头图像与音频的功能。
live555源码目录如下:
四个基本的库分别是:BasicUsageEnvironment, groupsock, liveMedia和UsageEnvironment。
编译后即生成这4个库文件:
这里我只简单说下liveMedia库的功能,其他三个库是live555运行的基础库,太(mei)简(yan)单(jiu),就不说了。
liveMedia库包含了音视频相关的所有功能,包含音视频文件的解析,RTP传输封装等,我们可以看到这个目录下有对h264、AAC等文件解析的支持:
交叉编译过程:略
这里我主要是修改mediaServer文件夹下的示例程序,添加实时预览摄像头图像与mic声音功能。
hi3516芯片,视频编码格式为h264,音频编码格式为AAC。
1.添加音频AAC支持
添加类 ADTSAudioLiveSource ,继承自FramedSource
在该类的doGetNextFrame函数里实现获取hi3516音频数据做为rtsp服务器音频源。
注意点:
1.1 adts默认是带7字节或者9字节的头,传给rtsp的时候是要去掉头的,实际上RTSP通过rtp传输AAC帧的时候是不带adts头的,而是带4个字节的mpeg4-generic头。
1.2 从FramedSource继承而来的变量
每次doGetNextFrame帧时,从FIFO里取一个完整的AAC帧,把帧拷贝到fTo buf里面,然后比较帧大小与fMaxSize来赋值几个关键的变量:
注意,不管帧长是否大于fMaxSize,每次都需要把完整的帧拷贝到fTo指针,live555内部会根据fNumTruncatedBytes等变量自行处理分包。
1.3 doGetNextFrame函数最后不管有没有取到帧,都需要执行FramedSource::afterGetting
1.4 采样率,通道数,configstr等的计算
这几个变量在mediaSubbsession建立RTPsink时要用到,它直接影响了SDP里对于AAC音频描述字段的产生
添加类 ,继承自
createNewStreamSource函数创建上面的ADTSAudioLiveSource做为音频输入源,参数estBitrate为预估的码率,海思AAC编码码率设置为24kbps,所以estBitrate设置为24.
createNewRTPSink有必要继承,因为需要根据音频源的采样率、通道数等创建RTPSink.
2.添加h264支持
添加 H264FramedLiveSource ,继承自FramedSource
unsigned maxFrameSize()函数必须继承,里面设置帧最大可能的大小,我设置为100000,如果不继承就是默认的,会出现画面马赛克
doGetNextFrame函数里面和AAC取帧的处理差不多,我加多了一个步骤,就是第一次取帧的时候会调用接口去产生一个关键帧,并且等待这个关键帧到来才处理,这样连接后出图会比较快。
添加类 ,继承自
这个类就是实现createNewStreamSource时创建H264FramedLiveSource
3.修改DynamicRTSPServer
修改类DynamicRTSPServer,在lookupServerMediaSession函数里动点手脚,默认在这个函数里面会根据文件名去寻找服务器下相应的文件做为直播源,我这里比较如果是我特定的live源名字则直接返回,相应的live源创建rtsp服务器的时候就添加好
4.初始化rtsp server
初始化rtsp服务器,添加一个ServerMediaSession,该mediaSession添加一个和一个,然后把该mediaSession添加给rtsp服务器。
客户端访问 rtsp://x.x.x.x/ch0.live 时就可以看到实时的摄像头图像与声音啦!
㈣ 编译android-vlc支持rtsp,是不是需要添加live555谁有详细的步骤呢给说下,最好有编译好的源码
vlc-android是直接支持rtsp的,可以播放rtsp。http,mms网络流 我编译好了一份源代码,你可以下载看看 http://download.csdn.net/detail/wng2010/4971056
㈤ Live555 源代码分析(二)
Live555对常用的socket操作进行了包装。
setupDatagramSocket()创建UDP socket。
setupStreamSocket()创建TCP socket。
increaseSendBufferTo()调用setsockopt(),增加发送缓存的大小。
writeSocket()和readSocket()分别发送和接收数据包。
ourIPAddress()得到本地地址。
RTPInterface负责发送、接收数据包。
TCP协议和UDP协议都可以承载数据包。一个RTPInterface实例可以同时支持多路TCP数据和多路UDP数据。对于TCP,一个TCP连接上可以有多个channel,每个channel对应一路数据。
RTPInterface自己处理TCP部分。这其中需要借助tcpStreamRecord和SocketDescriptor。
RTPInterface将UDP部分委托给Groupsock,一个GroupSock实例处理多个UDP连接。
因为不同channel共用同一个TCP连接,所以SocketDescriptor发送数据包时,会加上一个数据包头,其中对不同channel加以区分。
TCP连接上的数据格式如下图所示。
RTPInterface::addStreamSocket()创建tcpStreamRecord实例,并注册到对应的SocketDescriptor实例中,以便监控socket的可读数据。
RTPInterface::sendRTPorRTPPacketOverTCP(),向单个TCP连接发送数据包。
_Tables的成员socketTable保存了全局唯一的SocketDescriptor的hash表。
SocketDescriptor管理一个TCP socket,以及承载在它上面的channel。
对于SocketDescriptor,
SocketDescriptor::registerRTPInterface()还同时将SocketDescriptor::tcpReadHandler()设置为socket的监听函数。
当socket有数据可读时,SocketDescriptor::tcpReadHandler()被调用。
在tcpReadHandler1()中,
NetInterface和Socket定义了网络接口。
Port保存端口号,它的成员fPortNum是网络字节序的端口号。
对于NetInterface,
对于Socket,
Socket的构造函数调用setupDatagramSocket()创建socket,绑定本地端口号fPort,地址缺省为INADDR_ANY,即由协议层决定。
OutputSocket和GroupSock分别实现了write()和handleRead()。
对于OutputSocket,
对于GroupSock,
成员函数output()遍历fDests中的组播地址,向它们发送数据包。
成员函数handleRead()负责接收消息。它调用全局函数readSocket(),后者调用recvfrom()接收数据包。接收的数据通过handleRead()返回给调用者。
RTPInterface::sendPacket()发送数据包。
5.7.用RTPInterface接收数据
成员函数startNetworkReading()开始监听所有的TCP和UDP连接。调用者需要指定有数据可读时的回调函数。
当RTCPInterface的UDP或TCP socket有数据可读时,调用者指定的回调函数被调用。这时它应该调用RTCPInterface::handleRead()读取数据。
RTCPInstance从RTPSink的信息构造RTCP数据包,然后通过RTPInterface发送出去。OutPacketBuffer作为发送缓存使用。
对于OutPacketBuffer,
对于RTCPInstance,
RTCPInstance::sendReport()是发送数据包的例子。它先调用addReport()和addSDES()构造数据包,然后在调用sendBuiltPacket()发送它。sendBuiltPacket使用了RTPInterface::sendPacket()。
RTCPInstance::schele()设置一个超时任务。如果某些预期的事,没有在指定时间内发生,则触发指定的回调函数RTCPInstance::onExpire()进行重试。
RTCPInstace::onExpire()调用全局函数OnExpire()。在OnExpire()中,检查哪些事件没有如期发生,如发送BYE消息或其他消息没有成功,则重新尝试。
RTCPInstance::setByeHandler()设置一个回调函数,收到对应消息时,该回调函数被调用。这个回调函数保存在成员fByeHandlerTask。
在RTCPInstance的构造函数中,调用RTCPInterface::startNetworkReading()。其中将RTCPInstance::incomingReportHandler()设置为数据可读时的回调函数。
在RTCPInstance::incomingReportHandler()中,