[live555] testRTSPClient 通过ffmpeg 解码(记录)

live555 获取每一帧

testRTSPClient 中

Boolean DummySink::continuePlaying() {
  if (fSource == NULL) return False; // sanity check (should not happen)

  // Request the next frame of data from our input source.  "afterGettingFrame()" will get called later, when it arrives:
  fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
                        afterGettingFrame, this,
                        onSourceClosure, this);

  return True;
}

获取了fReceiveBuffer ,完整的一帧数据,可以选择在afterGettingFrame()中处理

在解码的过程中我使用了ffmpeg

将接收的每一帧保存成 h264 裸流文件(可用vlc 文件播放)

//打开文件 file.h264
File *fp_h264 = fopen("file.h264","ab+")//append 形式打开文件流

//初始化 参数  使用H264解码
av_register_all();
AVCodec *m_pAVCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
AVCodecContext  *m_pAVFrame = av_frame_alloc();
AVFrame  *m_pAVCodecContext = avcodec_alloc_context3(m_pAVCodec);

下面就是处理NALU 的转化为h264,通过0x00 00 00 01 区分NALU ,在testRTSPClient 的afterGettingFrame 中处理

  //定义一个结构体来存储一帧
  typedef struct {
	  unsigned char * frameBuf;
	  int framebufsize;
  }FrameUnit;
  FrameUnit *pFrameunit = NULL; 
  
  if (!strcmp(fSubsession.mediumName(), "video"))//只处理视频流
  {
     //给结构提分配内存
     pFrameunit = (FrameUnit*)malloc(sizeof(pFrameunit));
     
     char *pbuf = (char *)fReceiveBuffer;
     //每一帧之间的startcode 
     char head[4] = { 0x00, 0x00, 0x00, 0x01 };
     //在每一帧之间加上start code 
     //,0x0000001+sps+0x00000001+pps+0x00000001+NALU1+0x00000001+NALU2
     pFrameunit->framebufsize = frameSize + 4;
     pFrameunit->frameBuf = (unsigned char *)malloc(pFrameunit->framebufsize);
     memcpy(pFrameunit->frameBuf, head, 4);
     memcpy(pFrameunit->frameBuf + 4, pbuf, frameSize);
     //保存文件
     fwrite(pFrameunit->frameBuf, pFrameunit->framebufsize, 1, fp_h264);
     //释放内存
     free(pFrameunit->frameBuf);
     pFrameunit->frameBuf = NULL;
     pFrameunit->framebufsize = 0; 
     //当全部写完fclose
     fclose(fp_h264);
  }

以上就是完整的步骤,用vlc 播放file.h264 就可以
在这里插入图片描述

值得学习的是通过ffmpeg 解码转为YUV格式

同样的流程,不过在处理每一帧的数据时候,为ffmpeg的AVCodecContext添加extradata ,即 sps和pps
在处理每一帧数据之前将sps pps 解析出来并且添加到AVCodecContext中

//打开文件file.yuv
File *fp_yuv fopen("file.yuv"ab+")
//处理解码需要的sps 和pps
if (!strcmp(fSubsession.mediumName(), "video"))
  {
      //判断AVCodecContext->extradata 是否有数据
	  if (m_pAVCodecContext->extradata == NULL)
	  {
		  unsigned int SPropRecords = -1;
		  SPropRecord *p_record = parseSPropParameterSets(fSubsession.fmtp_spropparametersets(), SPropRecords);
         //sps pps 以数组的形式保存SPropRecord中
		  SPropRecord &sps = p_record[0];
		  SPropRecord &pps = p_record[1];
		  int totalsize = 0;
		  unsigned char* tmp = NULL;
		  unsigned char nalu_header[4] = { 0, 0, 0, 1 };

		  totalsize = 8 + sps.sPropLength + pps.sPropLength;
          //在每个sps 和pps 之前加上startcode 
		  tmp = (unsigned char*)realloc(tmp, totalsize);
		  memcpy(tmp, nalu_header, 4);
		  memcpy(tmp + 4, sps.sPropBytes, sps.sPropLength);
		  memcpy(tmp + 4 + sps.sPropLength, nalu_header, 4);
		  memcpy(tmp + 4 + sps.sPropLength + 4, pps.sPropBytes, pps.sPropLength);
          //将 sps 和pps 的数据给ffmpeg的h264解码器上下文
		  m_pAVCodecContext->extradata_size = totalsize;
		  m_pAVCodecContext->extradata = tmp;
	  }
	  //下面的步骤和保存h264一样,只是在保存文件的地方修改成解码
	  
      if (fp_yuv) {
      		AVPacket *avpkt = (AVPacket *)malloc(sizeof(AVPacket));
			AVFrame *pFrameYUV = av_frame_alloc();
			AVFrame *pFrameVideo = av_frame_alloc();
			int got_picture = 0;
			av_init_packet(avpkt);

		    //将每一帧赋值给AVPacket
			avpkt->data = frameunit->frameBuf;
			avpkt->size = frameunit->framebufsize;
			if (avpkt->size > 0)
			{
				//解码一帧,成功返回got_picture 1 ,解码数据在pFrameVideo
				int ret = avcodec_decode_video2(m_pAVCodecContext, pFrameVideo, &got_picture, avpkt);
				if (ret < 0) {
					env << "Decode :Error.\n";
					goto error_handler;
				}
			}
	        //解码成功,转化为 yuv 
			if (got_picture)
			{
			   //下面的步骤,将pFrameVideo转为pFrameYUV
				uint8_t * out_buffer = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, m_pAVCodecContext->width, m_pAVCodecContext->height));
				avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, m_pAVCodecContext->width, m_pAVCodecContext->height);
				struct SwsContext *img_convert_ctx = sws_getContext(m_pAVCodecContext->width, m_pAVCodecContext->height
					, m_pAVCodecContext->pix_fmt, m_pAVCodecContext->width, m_pAVCodecContext->height
					, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
				sws_scale(img_convert_ctx, (const unsigned char* const*)pFrameVideo->data, pFrameVideo->linesize, 0
					, m_pAVCodecContext->height, pFrameYUV->data, pFrameYUV->linesize);
			//输出出YUV数据  保存在file.yuv
               int y_size = m_pAVCodecContext->width * m_pAVCodecContext->height;
 				if (mFrameYUV->data[0])	
					fwrite(mFrameYUV->data[0], 1, y_size, fp_yuv);       //Y   
 				
 				if(mFrameYUV->data[1])
 					fwrite(mFrameYUV->data[1], 1, y_size / 4, fp_yuv);   //U  
				if(mFrameYUV->data[2])
					fwrite(mFrameYUV->data[2], 1, y_size / 4, fp_yuv);   //V  
				return 0;
			}
            //释放
			av_frame_free(&pFrameYUV);
			av_frame_free(&pFrameVideo);
			av_free_packet(avpkt);
			fclose(fp_yuv);
		}
		....
  }

选择yuv 播放器,选择720P格式播放
在这里插入图片描述

最后就是寻找一个类似SDL 来播放

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值