ffmpeg 接受socket数据为数据源

用ffmpeg写过一个自己的播放器,对ffmpeg如何打开打开文件,匹配demux,提取stream,匹配decode及渲染有一个感性的认识,目前想做一个网络接口支持多路远程摄像头的播放,起初的解决方案是dshow打开摄像头+x264编码+live555(RSTP)传输,发现编码速度太慢,达不到实时要求,于是将编码改成多线程,发现速度没什么提升(可能是拿出了问题吧)后来想起了ffmpeg(强悍的东东)于是暂停原来的方案,再次研究ffmpeg;这次的重点是配置io即用从自己设定的io中读取数据,想直接用socket发送编码数据,然后在客户端进行数据整合,传输到ffmpeg里进行解码输出;通常我们是按照下列步骤进行解码的:

 1打开视频文件:
首先第一件事情--让我们来看看怎样打开一个视频文件并从中得到流。我们要做的第一件事情就是初始化libavformat/libavcodec:

av_register_all();

这一步注册库中含有的所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。要注意你只需调用一次 av_register_all(),所以,尽可能的在你的初始代码中使用它。如果你愿意,你可以仅仅注册个人的文件格式和编码,不过,通常你不得不这么做却没有什么原因。

2
下一步,打开文件:
AVFormatContext *pFormatCtx;
const char    *filename="myvideo.mpg";
// 打开视频文件

if(av_open_input_file(&pFormatCtx,filename, NULL, 0, NULL)!=0)
       handle_error(); //
不能打开此文件


最后三个参数描述了文件格式,缓冲区大小(size)和格式参数;我们通过简单地指明NULL或0告诉 libavformat去自动探测文件格式并且使用默认的缓冲区大小。请在你的程序中用合适的出错处理函数替换掉handle_error()。


3下一步,我们需要取出包含在文件中的流信息:
// 取出流信息
if(av_find_stream_info(pFormatCtx)<0)
     handle_error(); //
不能够找到流信息

这一步会用有效的信息把 AVFormatContext 的流域(streams field)填满。作为一个可调试的诊断,我们会将这些信息全盘输出到标准错误输出中,不过你在一个应用程序的产品中并不用这么做:
dump_format(pFormatCtx, 0, filename, false);

就像在引言中提到的那样,我们仅仅处理视频流,而不是音频流。为了让这件事情更容易理解,我们只简单使用我们发现的第一种视频流:

int i, videoStream;
AVCodecContext *pCodecCtx;
//  
寻找第一个视频流
videoStream=-1;
for(i=0; inb_streams; i++)
if(pFormatCtx->streams->codec.codec_type==CODEC_TYPE_VIDEO)
{
       videoStream=i;
       break;
}
if(videoStream==-1)
handle_error(); // Didn't find a video stream

//
得到视频流编码上下文的指针
pCodecCtx=&pFormatCtx->streams[videoStream]->codec;

好了,我们已经得到了一个指向视频流的称之为上下文的指针。但是我们仍然需要找到真正的编码器打开它。


寻找视频流的解码器
AVCodec *pCodec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
handle_error(); //
找不到解码器

//
通知解码器我们能够处理截断的bit流--ie,
// bit流帧边界可以在包中
if(pCodec->capabilities & CODEC_CAP_TRUNCATED)
pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;

5
打开解码器
if(avcodec_open(pCodecCtx, pCodec)<0)
handle_error(); // 打不开解码器(那么什么是“截断bit流”?好的,就像一会我们看到的,视频流中的数据是被分割放入包中的。因为每个视频帧的数据的大小是可变的,那么两帧之间的边界就不一定刚好是包的边界。这里,我们告知解码器我们可以处理bit流。)



存储在 AVCodecContext结构中的一个重要的信息就是视频帧速率。为了允许非整数的帧速率(比如 NTSC的   29.97帧),速率以分数的形式存储,分子在 pCodecCtx->frame_rate,分母在pCodecCtx->frame_rate_base 中。在用不同的视频文件测试库时,我注意到一些编码器(很显然ASF)似乎并不能正确的给予赋值( frame_rate_base 用1代替1000)。下面给出修复补丁:

//  
加入这句话来纠正某些编码器产生的帧速错误
if(pCodecCtx->frame_rate>1000 &&pCodecCtx->frame_rate_base==1)
pCodecCtx->frame_rate_base=1000;

注意即使将来这个bug解决了,留下这几句话也并没有什么坏处。视频不可能拥有超过1000fps的帧速。

6
给视频帧分配空间以便存储解码后的图片:

AVFrame *pFrame;

pFrame=avcodec_alloc_frame();

7最后,我们已经准备好来从流中读取数据了。

 

读取数据

 

我们将要做的是通过读取包来读取整个视频流,然后把它解码成帧,最好后转换格式并且保存。

 

int frameFinished;

 

AVPacket packet;

 

i=0;

 

while(av_read_frame(pFormatCtx,&packet)>=0) {

 

// Is this a packet from the video stream?

 

if(packet.stream_index==videoStream) {

 

// Decode video frame

 

avcodec_decode_video(pCodecCtx, pFrame,&frameFinished,

 

packet.data, packet.size);

 

// Did we get a video frame?

 

if(frameFinished) {

 

// Convert the image from its native formatto RGB

 

img_convert((AVPicture *)pFrameRGB,PIX_FMT_RGB24,

 

(AVPicture*)pFrame, pCodecCtx->pix_fmt,

 

pCodecCtx->width, pCodecCtx->height);

 

// Save the frame to disk

 

if(++i<=5)

 

SaveFrame(pFrameRGB, pCodecCtx->width,

 

pCodecCtx->height, i);

 

}

 

}

 

// Free the packet that was allocated byav_read_frame

 

av_free_packet(&packet);

 

}

 

这个循环过程是比较简单的:av_read_frame()读取一个包并且把它保存到AVPacket结构体中。注意我们仅仅申请了一个包的结构体――ffmpeg为我们申请了内部的数据的内存并通过packet.data指针来指向它。这些数据可以在后面通过av_free_packet()来释放。函数avcodec_decode_video()把包转换为帧。然而当解码一个包的时候,我们可能没有得到我们需要的关于帧的信息。因此,当我们得到下一帧的时候,avcodec_decode_video()为我们设置了帧结束标志frameFinished。最后,我们使用 img_convert()函数来把帧从原始格式(pCodecCtx->pix_fmt)转换成为RGB格式。要记住,你可以把一个 AVFrame结构体的指针转换为AVPicture结构体的指针。最后,我们把帧和高度宽度信息传递给我们的SaveFrame函数。

然而现在我想配置自己的“文件源“(socket传过来的数据),好在ffmpeg不但支持匹配demuxdecoder而且还给出了数据源的接口;

首先生成自己的AVIOContext

         play->m_avio_ctx =avio_alloc_context(play->m_io_buffer,

                   IO_BUFFER_SIZE, 0, (void*)play,read_packet, NULL, seek_packet);

         if (!play->m_io_buffer)

         {

                   printf("Create iocontext failed!\n");

                   av_free(play->m_io_buffer);

                   return -1;

         }

         play->m_avio_ctx->write_flag = 0;

 

         ret = av_probe_input_buffer(play->m_avio_ctx,&iformat, "", NULL, 0, NULL);

         if (ret < 0)

         {

                   printf("av_probe_input_buffercall failed!\n");

                   goto FAILED_FLG;

         }

 

//关键点在于配置pb

         play->m_format_ctx->pb = play->m_avio_ctx;

下面就和以前一样了

         ret = avformat_open_input(&play->m_format_ctx, "", iformat, NULL);

不知不觉到时间了,时间比较匆促就写这些啦,有时间再补充

 



你可能感兴趣的:(ffmpeg,数据源)