嵌入式设备上ffmpeg取摄像头+硬编码+rtmp推送

手中的板子的CPU是三星coretex-a9的,板子有硬编能力(至于是哪个芯片没仔细看),通过一至两周的努力,成功实现硬编码成H.264并投递到rtmp服务器。大体实现如下:

1.使用ffmpeg取摄像头YUV数据;

2.将YUV数据喂给三星硬编API;

3.将硬编的数据通过librtmp投递;

废话少说,上代码,代码有些糙,将就看:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>

#include <librtmp/rtmp.h>

#include <nx_fourcc.h>
#include <nx_vip.h>   // VIP
#include <nx_dsp.h>  // Display
#include "nx_video_api.h" // Video En/Decoder

#include "sps_decode.h"

#define MAX_FILE_NAME   1024
#define MAX_SEQ_BUF_SIZE  (4*1024)

//定义包头长度,RTMP_MAX_HEADER_SIZE=18
#define RTMP_HEAD_SIZE   (sizeof(RTMPPacket)+RTMP_MAX_HEADER_SIZE)
//存储Nal单元数据的buffer大小
#define BUFFER_SIZE 327680
#define MAX_PACKET_BYTES (RTMP_HEAD_SIZE+BUFFER_SIZE)
//搜寻Nal单元时的一些标志
#define GOT_A_NAL_CROSS_BUFFER BUFFER_SIZE+1
#define GOT_A_NAL_INCLUDE_A_BUFFER BUFFER_SIZE+2
#define NO_MORE_BUFFER_TO_READ BUFFER_SIZE+3
enum 

  VIDEO_CODECID_H264 = 7, 
}; 
/**
 * _NaluUnit
 * 内部结构体。该结构体主要用于存储和传递Nal单元的类型、大小和数据
 */
typedef struct _NaluUnit 

 int type; 
    int size; 
 unsigned char *data; 
}NaluUnit;

typedef struct _RTMPMetadata
{
 // video, must be h264 type
 unsigned int nWidth;
 unsigned int nHeight;
 unsigned int nFrameRate;  // fps
 unsigned int nVideoDataRate; // bps
 unsigned int nSpsLen;
 unsigned char* Sps;
 unsigned int nPpsLen;
 unsigned char* Pps;

 // audio, must be aac type
 bool         bHasAudio;
 unsigned int nAudioDatarate;
 unsigned int nAudioSampleRate;
 unsigned int nAudioSampleSize;
 int    nAudioFmt;
 unsigned int nAudioChannels;
 char      pAudioSpecCfg;
 unsigned int nAudioSpecCfgLen;

} RTMPMetadata,*LPRTMPMetadata;

static void dumpdata( void *data, int len, const char *msg )
{
 int i=0;
 unsigned char *byte = (unsigned char *)data;
 printf("Dump Data : %s", msg);
 for( i=0 ; i<len ; i ++ )
 {
  if( i!=0 && i%16 == 0 ) printf("\n\t");
  printf("%.2x", byte[i] );
  if( i%4 == 3 ) printf(" ");
 }
 printf("\n");
}


//网络字节序转换
char * put_byte( char *output, uint8_t nVal )   
{   
 output[0] = nVal;   
 return output+1;   
}  

char * put_be16(char *output, uint16_t nVal )   
{   
 output[1] = nVal & 0xff;   
 output[0] = nVal >> 8;   
 return output+2;   

char * put_be24(char *output,uint32_t nVal )   
{   
 output[2] = nVal & 0xff;   
 output[1] = nVal >> 8;   
 output[0] = nVal >> 16;   
 return output+3;   
}   
char * put_be32(char *output, uint32_t nVal )   
{   
 output[3] = nVal & 0xff;   
 output[2] = nVal >> 8;   
 output[1] = nVal >> 16;   
 output[0] = nVal >> 24;   
 return output+4;   
}   
char *  put_be64( char *output, uint64_t nVal )   
{   
 output=put_be32( output, nVal >> 32 );   
 output=put_be32( output, nVal );   
 return output;   

char * put_amf_string( char *c, const char *str )   
{   
 uint16_t len = strlen( str );   
 c=put_be16( c, len );   
 memcpy(c,str,len);   
 return c+len;   
}   
char * put_amf_double( char *c, double d )   
{   
 *c++ = AMF_NUMBER;  /* type: Number */   
 {   
  unsigned char *ci, *co;   
  ci = (unsigned char *)&d;   
  co = (unsigned char *)c;   
  co[0] = ci[7];   
  co[1] = ci[6];   
  co[2] = ci[5];   
  co[3] = ci[4];   
  co[4] = ci[3];   
  co[5] = ci[2];   
  co[6] = ci[1];   
  co[7] = ci[0];   
 }   
 return c+8;   

unsigned int g_nStop;

unsigned int  m_nFileBufSize;
unsigned int  nalhead_pos;
RTMP* m_pRtmp; 
RTMPMetadata g_metaData;
char m_pBuff[MAX_PACKET_BYTES];
unsigned char *m_pFileBuf; 
unsigned char *m_pFileBuf_tmp;
unsigned char* m_pFileBuf_tmp_old; //used for realloc

/**
 * 初始化并连接到服务器
 *
 * @param url 服务器上对应webapp的地址
 *     
 * @成功则返回1 , 失败则返回0
 */
int RTMP264_Connect(const char* url) 

 nalhead_pos=0;
 m_nFileBufSize=BUFFER_SIZE;
 m_pFileBuf=(unsigned char*)malloc(BUFFER_SIZE);
 m_pFileBuf_tmp=(unsigned char*)malloc(BUFFER_SIZE);

 m_pRtmp = RTMP_Alloc();
 RTMP_Init(m_pRtmp);
 /*设置URL*/
 if (RTMP_SetupURL(m_pRtmp,(char*)url) == FALSE)
 {
  RTMP_Free(m_pRtmp);
  return false;
 }
 /*设置可写,即发布流,这个函数必须在连接前使用,否则无效*/
 RTMP_EnableWrite(m_pRtmp);
 /*连接服务器*/
 if (RTMP_Connect(m_pRtmp, NULL) == FALSE)
 {
  RTMP_Free(m_pRtmp);
  return false;
 }

 /*连接流*/
 if (RTMP_ConnectStream(m_pRtmp,0) == FALSE)
 {
  RTMP_Close(m_pRtmp);
  RTMP_Free(m_pRtmp);
  return false;
 }
 return true; 


/**
 * 断开连接,释放相关的资源。
 *
 */   
void RTMP264_Close() 

 if(m_pRtmp) 
 { 
  RTMP_Close(m_pRtmp); 
  RTMP_Free(m_pRtmp); 
  m_pRtmp = NULL; 
 } 
 if (m_pFileBuf != NULL)
 { 
  free(m_pFileBuf);
 } 
 if (m_pFileBuf_tmp != NULL)
 { 
  free(m_pFileBuf_tmp);
 }
}

/**
 * 发送RTMP数据包
 *
 * @param nPacketType 数据类型
 * @param data 存储数据内容
 * @param size 数据大小
 * @param nTimestamp 当前包的时间戳
 *
 * @成功则返回 1 , 失败则返回一个小于0的数
 */
int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp) 

    if(m_pRtmp == NULL) return -1;
 RTMPPacket* packet;
 /*分配包内存和初始化,len为包体长度*/
 packet = (RTMPPacket *)m_pBuff;
 
 /*包体内存*/
 packet->m_body = (char *)packet + RTMP_HEAD_SIZE;
 packet->m_nBodySize = size;
 //memcpy(packet->m_body,data,size);
 packet->m_hasAbsTimestamp = 0;
 packet->m_packetType = nPacketType; /*此处为类型有两种一种是音频,一种是视频*/
 packet->m_nInfoField2 = m_pRtmp->m_stream_id;
 packet->m_nChannel = 0x04;

 packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
 if (RTMP_PACKET_TYPE_AUDIO ==nPacketType && size !=4)
 {
  packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
 }
 packet->m_nTimeStamp = nTimestamp;
 /*发送*/
 int nRet =0;
 if (RTMP_IsConnected(m_pRtmp))
 {
  nRet = RTMP_SendPacket(m_pRtmp,packet,TRUE); /*TRUE为放进发送队列,FALSE是不放进发送队列,直接发送*/
 }

 return nRet; 

/**
 * 发送视频的sps和pps信息
 *
 * @param pps 存储视频的pps信息
 * @param pps_len 视频的pps信息长度
 * @param sps 存储视频的pps信息
 * @param sps_len 视频的sps信息长度
 *
 * @成功则返回 1 , 失败则返回0
 */
int SendVideoSpsPps(unsigned char *pps,int pps_len,unsigned char * sps,int sps_len)
{
    //dumpdata(pps, pps_len, "pps: ");
    //dumpdata(sps, sps_len, "sps: ");
    if(m_pRtmp == NULL)
    {
        return 0;
    }
 RTMPPacket * packet=NULL;//rtmp包结构
 unsigned char * body=NULL;
 int i;
 packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE+1024);
 //RTMPPacket_Reset(packet);//重置packet状态
 memset(packet,0,RTMP_HEAD_SIZE+1024);
 packet->m_body = (char *)packet + RTMP_HEAD_SIZE;
 body = (unsigned char *)packet->m_body;
 i = 0;
 body[i++] = 0x17;
 body[i++] = 0x00;

 body[i++] = 0x00;
 body[i++] = 0x00;
 body[i++] = 0x00;

 /*AVCDecoderConfigurationRecord*/
 body[i++] = 0x01;
 body[i++] = sps[1];
 body[i++] = sps[2];
 body[i++] = sps[3];
 body[i++] = 0xff;

 /*sps*/
 body[i++]   = 0xe1;
 body[i++] = (sps_len >> 8) & 0xff;
 body[i++] = sps_len & 0xff;
 memcpy(&body[i],sps,sps_len);
 i +=  sps_len;

 /*pps*/
 body[i++]   = 0x01;
 body[i++] = (pps_len >> 8) & 0xff;
 body[i++] = (pps_len) & 0xff;
 memcpy(&body[i],pps,pps_len);
 i +=  pps_len;

 packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
 packet->m_nBodySize = i;
 packet->m_nChannel = 0x04;
 packet->m_nTimeStamp = 0;
 packet->m_hasAbsTimestamp = 0;
 packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
 packet->m_nInfoField2 = m_pRtmp->m_stream_id;

 /*调用发送接口*/
 int nRet = 0;
 if (RTMP_IsConnected(m_pRtmp))
 {
     nRet = RTMP_SendPacket(m_pRtmp,packet,TRUE);
    }
 free(packet);    //释放内存
 return nRet;
}

/**
 * 发送H264数据帧
 *
 * @param data 存储数据帧内容
 * @param size 数据帧的大小
 * @param bIsKeyFrame 记录该帧是否为关键帧
 * @param nTimeStamp 当前帧的时间戳
 *
 * @成功则返回 1 , 失败则返回0
 */
int SendH264Packet(unsigned char *data,unsigned int size,int bIsKeyFrame,unsigned int nTimeStamp) 

 if(data == NULL && size<11){ 
  return false; 
 } 

 unsigned char *body = (unsigned char*)(m_pBuff+RTMP_HEAD_SIZE); 
 
 int i = 0;
 if(bIsKeyFrame){ 
  body[i++] = 0x17;// 1:Iframe  7:AVC  
  body[i++] = 0x01;// AVC NALU  
  body[i++] = 0x00; 
  body[i++] = 0x00; 
  body[i++] = 0x00; 


  // NALU size  
  body[i++] = size>>24 &0xff; 
  body[i++] = size>>16 &0xff; 
  body[i++] = size>>8 &0xff; 
  body[i++] = size&0xff;
  // NALU data  
  memcpy(&body[i],data,size); 
  SendVideoSpsPps(g_metaData.Pps,g_metaData.nPpsLen,g_metaData.Sps,g_metaData.nSpsLen);
 }else{ 
  body[i++] = 0x27;// 2:Pframe  7:AVC  
  body[i++] = 0x01;// AVC NALU  
  body[i++] = 0x00; 
  body[i++] = 0x00; 
  body[i++] = 0x00; 


  // NALU size  
  body[i++] = size>>24 &0xff; 
  body[i++] = size>>16 &0xff; 
  body[i++] = size>>8 &0xff; 
  body[i++] = size&0xff;
  // NALU data  
  memcpy(&body[i],data,size); 
 } 
 
 int bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp); 

 return bRet; 
}

/**
 * 从内存中读取出第一个Nal单元
 *
 * @param nalu 存储nalu数据
 * @param read_buffer 回调函数,当数据不足的时候,系统会自动调用该函数获取输入数据。
 *     2个参数功能:
 *     uint8_t *buf:外部数据送至该地址
 *     int buf_size:外部数据大小
 *     返回值:成功读取的内存大小
 * @成功则返回 1 , 失败则返回0
 */
int ReadFirstNaluFromBuf(NaluUnit *nalu,int (*read_buffer)(uint8_t *buf, int buf_size))
{
 int naltail_pos=nalhead_pos;
 memset(m_pFileBuf_tmp,0,BUFFER_SIZE);
 while(nalhead_pos<m_nFileBufSize) 
 { 
  //search for nal header
  if(m_pFileBuf[nalhead_pos++] == 0x00 &&
   m_pFileBuf[nalhead_pos++] == 0x00)
  {
   if(m_pFileBuf[nalhead_pos++] == 0x01)
    goto gotnal_head;
   else
   {
    //cuz we have done an i++ before,so we need to roll back now
    nalhead_pos--;  
    if(m_pFileBuf[nalhead_pos++] == 0x00 &&
     m_pFileBuf[nalhead_pos++] == 0x01)
     goto gotnal_head;
    else
     continue;
   }
  }
  else
   continue;

  //search for nal tail which is also the head of next nal
gotnal_head:
  //normal case:the whole nal is in this m_pFileBuf
  naltail_pos = nalhead_pos; 
  while (naltail_pos<m_nFileBufSize) 
  { 
   if(m_pFileBuf[naltail_pos++] == 0x00 &&
    m_pFileBuf[naltail_pos++] == 0x00 )
   { 
    if(m_pFileBuf[naltail_pos++] == 0x01)
    {
     nalu->size = (naltail_pos-3)-nalhead_pos;
     break;
    }
    else
    {
     naltail_pos--;
     if(m_pFileBuf[naltail_pos++] == 0x00 &&
      m_pFileBuf[naltail_pos++] == 0x01)
     { 
      nalu->size = (naltail_pos-4)-nalhead_pos;
      break;
     }
    }
   } 
  }

  nalu->type = m_pFileBuf[nalhead_pos]&0x1f;
  memcpy(m_pFileBuf_tmp,m_pFileBuf+nalhead_pos,nalu->size);
  nalu->data=m_pFileBuf_tmp;
  nalhead_pos=naltail_pos;
  return TRUE;     
 }
}

/**
 * 从内存中读取出一个Nal单元
 *
 * @param nalu 存储nalu数据
 * @param read_buffer 回调函数,当数据不足的时候,系统会自动调用该函数获取输入数据。
 *     2个参数功能:
 *     uint8_t *buf:外部数据送至该地址
 *     int buf_size:外部数据大小
 *     返回值:成功读取的内存大小
 * @成功则返回 1 , 失败则返回0
 */
int ReadOneNaluFromBuf(NaluUnit *nalu,int (*read_buffer)(uint8_t *buf, int buf_size)) 
{   
 
 int naltail_pos=nalhead_pos;
 int ret;
 int nalustart;//nal的开始标识符是几个00
 memset(m_pFileBuf_tmp,0,BUFFER_SIZE);
 nalu->size=0;
 while(1)
 {
  if(nalhead_pos==NO_MORE_BUFFER_TO_READ)
   return FALSE;
  while(naltail_pos<m_nFileBufSize) 
  { 
   //search for nal tail
   if(m_pFileBuf[naltail_pos++] == 0x00 &&
    m_pFileBuf[naltail_pos++] == 0x00)
   {
    if(m_pFileBuf[naltail_pos++] == 0x01)
    { 
     nalustart=3;
     goto gotnal ;
    }
    else
    {
     //cuz we have done an i++ before,so we need to roll back now
     naltail_pos--;  
     if(m_pFileBuf[naltail_pos++] == 0x00 &&
      m_pFileBuf[naltail_pos++] == 0x01)
     {
      nalustart=4;
      goto gotnal;
     }
     else
      continue;
    }
   }
   else
    continue;

   gotnal: 
     /**
     *special case1:parts of the nal lies in a m_pFileBuf and we have to read from buffer
     *again to get the rest part of this nal
     */
    if(nalhead_pos==GOT_A_NAL_CROSS_BUFFER || nalhead_pos==GOT_A_NAL_INCLUDE_A_BUFFER)
    {
     nalu->size = nalu->size+naltail_pos-nalustart;
     if(nalu->size>BUFFER_SIZE)
     {
      m_pFileBuf_tmp_old=m_pFileBuf_tmp; //// save pointer in case realloc fails
      if((m_pFileBuf_tmp = (unsigned char*)realloc(m_pFileBuf_tmp,nalu->size)) ==  NULL )
      {
       free( m_pFileBuf_tmp_old );  // free original block
       return FALSE;
      }
     }
     memcpy(m_pFileBuf_tmp+nalu->size+nalustart-naltail_pos,m_pFileBuf,naltail_pos-nalustart);
     nalu->data=m_pFileBuf_tmp;
     nalhead_pos=naltail_pos;
     return TRUE;
    }
    //normal case:the whole nal is in this m_pFileBuf
    else
    { 
     nalu->type = m_pFileBuf[nalhead_pos]&0x1f;
     nalu->size=naltail_pos-nalhead_pos-nalustart;
     if(nalu->type==0x06)
     {
      nalhead_pos=naltail_pos;
      continue;
     }
     memcpy(m_pFileBuf_tmp,m_pFileBuf+nalhead_pos,nalu->size);
     nalu->data=m_pFileBuf_tmp;
     nalhead_pos=naltail_pos;
     return TRUE;   
    }      
  }

  if(naltail_pos>=m_nFileBufSize && nalhead_pos!=GOT_A_NAL_CROSS_BUFFER && nalhead_pos != GOT_A_NAL_INCLUDE_A_BUFFER)
  {
   nalu->size = BUFFER_SIZE-nalhead_pos;
   nalu->type = m_pFileBuf[nalhead_pos]&0x1f;
   memcpy(m_pFileBuf_tmp,m_pFileBuf+nalhead_pos,nalu->size);
   if((ret=read_buffer(m_pFileBuf,m_nFileBufSize))<BUFFER_SIZE)
   {
    memcpy(m_pFileBuf_tmp+nalu->size,m_pFileBuf,ret);
    nalu->size=nalu->size+ret;
    nalu->data=m_pFileBuf_tmp;
    nalhead_pos=NO_MORE_BUFFER_TO_READ;
    return FALSE;
   }
   naltail_pos=0;
   nalhead_pos=GOT_A_NAL_CROSS_BUFFER;
   continue;
  }
  if(nalhead_pos==GOT_A_NAL_CROSS_BUFFER || nalhead_pos == GOT_A_NAL_INCLUDE_A_BUFFER)
  {
   nalu->size = BUFFER_SIZE+nalu->size;
    
    m_pFileBuf_tmp_old=m_pFileBuf_tmp; //// save pointer in case realloc fails
    if((m_pFileBuf_tmp = (unsigned char*)realloc(m_pFileBuf_tmp,nalu->size)) ==  NULL )
    {
     free( m_pFileBuf_tmp_old );  // free original block
     return FALSE;
    }

   memcpy(m_pFileBuf_tmp+nalu->size-BUFFER_SIZE,m_pFileBuf,BUFFER_SIZE);
   
   if((ret=read_buffer(m_pFileBuf,m_nFileBufSize))<BUFFER_SIZE)
   {
    memcpy(m_pFileBuf_tmp+nalu->size,m_pFileBuf,ret);
    nalu->size=nalu->size+ret;
    nalu->data=m_pFileBuf_tmp;
    nalhead_pos=NO_MORE_BUFFER_TO_READ;
    return FALSE;
   }
   naltail_pos=0;
   nalhead_pos=GOT_A_NAL_INCLUDE_A_BUFFER;
   continue;
  }
 }
 return FALSE; 
}

/**
 * 将内存中的一段H.264编码的视频数据利用RTMP协议发送到服务器
 *
 * @param read_buffer 回调函数,当数据不足的时候,系统会自动调用该函数获取输入数据。
 *     2个参数功能:
 *     uint8_t *buf:外部数据送至该地址
 *     int buf_size:外部数据大小
 *     返回值:成功读取的内存大小
 * @成功则返回1 , 失败则返回0
 */
int RTMP264_Send(int (*read_buffer)(unsigned char *buf, int buf_size)) 
{   
 int ret;
 uint32_t now,last_update;
  
 memset(&g_metaData,0,sizeof(RTMPMetadata));
 memset(m_pFileBuf,0,BUFFER_SIZE);
 if((ret=read_buffer(m_pFileBuf,m_nFileBufSize))<0)
 {
  return FALSE;
 }

 NaluUnit naluUnit; 
 // 读取SPS帧  
 ReadFirstNaluFromBuf(&naluUnit,read_buffer); 
 g_metaData.nSpsLen = naluUnit.size; 
 g_metaData.Sps=(unsigned char*)malloc(naluUnit.size);
 memcpy(g_metaData.Sps,naluUnit.data,naluUnit.size);
    dumpdata(g_metaData.Sps, g_metaData.nSpsLen, "SPS: ");
   
 // 读取PPS帧  
 ReadOneNaluFromBuf(&naluUnit,read_buffer); 
 g_metaData.nPpsLen = naluUnit.size;
 g_metaData.Pps=(unsigned char*)malloc(naluUnit.size);
 memcpy(g_metaData.Pps,naluUnit.data,naluUnit.size);
 dumpdata(g_metaData.Pps, g_metaData.nPpsLen, "PPS: ");
 
 // 解码SPS,获取视频图像宽、高信息  
 int width = 0,height = 0, fps=0; 
 h264_decode_sps(g_metaData.Sps,g_metaData.nSpsLen,&width,&height,&fps); 
 printf("00width:%d,height:%d, fps:%d\n", width, height, fps);
 //g_metaData.nWidth = width; 
 //g_metaData.nHeight = height; 
 if(fps)
  g_metaData.nFrameRate = fps;
 else
  g_metaData.nFrameRate = 30;


    printf("width:%d,height:%d, fps:%d\n", width, height, fps);
 //发送PPS,SPS
 //ret=SendVideoSpsPps(g_metaData.Pps,g_metaData.nPpsLen,g_metaData.Sps,g_metaData.nSpsLen);
 //if(ret!=1)
 // return FALSE;

 unsigned int tick = 0; 
 unsigned int tick_gap = 1000/g_metaData.nFrameRate;
 ReadOneNaluFromBuf(&naluUnit,read_buffer);
 int bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;
 while(SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick)) 
 {   
got_sps_pps:
  if(!ReadOneNaluFromBuf(&naluUnit,read_buffer))
    goto end;
  if(naluUnit.type == 0x07 || naluUnit.type == 0x08)
   goto got_sps_pps;
  bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;
  tick +=tick_gap;
  //msleep(tick_gap-now+last_update); 
  sleep(40);
 } 
 end:
 free(g_metaData.Sps);
 free(g_metaData.Pps);
 return TRUE; 

//解析硬编码 NX_VidEncGetSeqInfo 中的SPS和PPS
int decode_sps_pps(RTMPMetadata* pMeta,unsigned char * buff, int size)
{
    dumpdata(buff, size, "SEQ of H264: ");
   
    pMeta->nSpsLen = 9; 
 pMeta->Sps=(unsigned char*)malloc(pMeta->nSpsLen);
 memcpy(pMeta->Sps,buff+4, pMeta->nSpsLen);
    dumpdata(pMeta->Sps, pMeta->nSpsLen, "SPS: ");
   
    pMeta->nPpsLen = 4; 
 pMeta->Pps=(unsigned char*)malloc(4);
 memcpy(pMeta->Pps,buff+17,4);
 dumpdata(pMeta->Pps, pMeta->nPpsLen, "PPS: ");
 
    return 0;
}

FILE *fp_send1; 
 
//读文件的回调函数 
//we use this callback function to read data from buffer 
int read_buffer1(unsigned char *buf, int buf_size ){ 
    if(!feof(fp_send1)){ 
        int true_size=fread(buf,1,buf_size,fp_send1); 
        return true_size; 
    }else{ 
        return -1; 
    } 

void version()
{
    fprintf(stdout, "V1.0\n");
}
void usage(char* app)
{
    fprintf(stdout, "\t-i input device name:/dev/video0\n");
    fprintf(stdout, "\t-y record YUV file name : out.yuv \n");
    fprintf(stdout, "\t-r record H.264 file name: out.h264\n");
    fprintf(stdout, "\t-u url of rtmp: rtmp://192.168.1.102:1935/hls/cam1\n");
    fprintf(stdout, "\t-v show version.\n");
    fprintf(stdout, "\t-h show help.\n");
    fprintf(stdout, "for examples:\t-i /dev/video10 -u rtmp://192.168.1.6/hls/cam1\n");
    fprintf(stdout, "\t\t-i /dev/video10 -y 1.yuv\n");
    fprintf(stdout, "\t\t-i /dev/video10 -r 1.h264\n");
}

void my_exit(int s){ 
    g_nStop = 1;

int main(int argc, char* argv[])
{
    g_nStop = 0;
    struct sigaction sigIntHandler; 
    sigIntHandler.sa_handler = my_exit; 
    sigemptyset(&sigIntHandler.sa_mask); 
    sigIntHandler.sa_flags = 0; 
    sigaction(SIGINT, &sigIntHandler, NULL); 


    FILE *fp_h264 = NULL;
    FILE *fp_yuv = NULL;
   
   
    char input_name[MAX_FILE_NAME+1] = {0};
    char yuv_name[MAX_FILE_NAME+1] = {0};
    char h264_name[MAX_FILE_NAME+1] = {0};
    char rtmp_url[MAX_FILE_NAME+1] = {0};
   
    char video_size[20] = {0};
    strcpy(video_size, "320x240");
    int bit_rate = 200; //kbit
   
    int   opt;
    while( (opt = getopt(argc,argv,"hvi:y:r:u:s:b:")) != -1 )     //解析结束时,返回-1
    {
        switch(opt)
        {
            case 'b':
                bit_rate = atol(optarg);
                break;
            case 'h':
                usage(argv[0]);
                return 0;
            case 'v':
                version();
                return 0;
            case 'i':
                strncpy(input_name, optarg, MAX_FILE_NAME);
                break;
            case 'y':
                strncpy(yuv_name, optarg, MAX_FILE_NAME);
                fp_yuv = fopen(yuv_name, "wb+");
                break;
            case 'r':
                strncpy(h264_name, optarg, MAX_FILE_NAME);
                fp_h264 = fopen(h264_name, "wb+");
                break;
            case 'u':
                strncpy(rtmp_url, optarg, MAX_FILE_NAME);
                break;
            case 's':
                strncpy(video_size, optarg, 20);
                break;
        }
    }
   
    int ret = 0;
    av_register_all();
    avcodec_register_all();
    avformat_network_init();
    avdevice_register_all();
   
    if(strlen(rtmp_url)>0)
    {
        ret = RTMP264_Connect(rtmp_url);
        if(!ret)
        {
            printf("RTMP264_Connect fail! %s\n", rtmp_url);
            return -1;
        }
    }
   
    if(strstr(input_name, ".h264")!=NULL)
    {
        fp_send1 = fopen(input_name, "rb");
        RTMP264_Send(read_buffer1);
        return 0;
   }

   int videoindex = -1;
   
    AVFormatContext *ifmt_ctx = NULL;
    AVCodecContext  *pCodecCtx;
    AVCodec         *pCodec;
    AVInputFormat *ifmt = NULL; //输入
    AVDictionary *format_opts;
    av_dict_set(&format_opts,"video_size", video_size, 0);
  
    ifmt_ctx = avformat_alloc_context();
    ifmt = av_find_input_format("video4linux2");
    if(ifmt == NULL)
    {
        printf("Couldn't find video4linux2.\n");
    }
    if(avformat_open_input(&ifmt_ctx, input_name, ifmt, &format_opts) != 0)
    {
        printf("Couldn't open input stream.\n");
        return -1;
    }
    if(avformat_find_stream_info(ifmt_ctx, NULL) <0)
    {
        printf("Couldn't find stream information.\n");
     return -1;
    }

    int i;
    printf("stream count=%d .\n", ifmt_ctx->nb_streams);
    for(i=0; i<ifmt_ctx->nb_streams; i++)
    {      
     if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            printf("codec_type:%d\n", ifmt_ctx->streams[i]->codec->codec_type);
            videoindex=i;
            //break;
        }
    }
    if(videoindex == -1)
    {
         printf("Couldn't find a video stream.\n");
         return -1;
    }

   
    pCodecCtx = ifmt_ctx->streams[videoindex]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL)
    {
        printf("Codec not found.(没有找到解码器)\n");
        return -1;
    }
    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
    {
        printf("Could not open codec.(无法打开解码器)\n");
        return -1;
    }
  
   
 int inWidth, inHeight;  // Clipper Output Information
 int frameCnt = 0;
 
 // Encoder Parameters
 NX_VID_ENC_INIT_PARAM encInitParam;
 unsigned char *seqBuffer = (unsigned char *)malloc( MAX_SEQ_BUF_SIZE );
 NX_VID_ENC_HANDLE hEnc;
 NX_VID_ENC_IN encIn;
 NX_VID_ENC_OUT encOut;


 long long totalSize = 0;
 double bitRate = 0.;
 long long vipTimeStamp;

 int instanceIdx;

 // Set Image & Clipper Information
 inWidth = pCodecCtx->width;
 inHeight = pCodecCtx->height;
 
 fprintf(stdout, "inWidth=%d, inHeight=%d\n", inWidth, inHeight);
 
    // Initialize Encoder
 hEnc = NX_VidEncOpen( NX_AVC_ENC,  &instanceIdx);

 memset( &encInitParam, 0, sizeof(encInitParam) );
 encInitParam.width = inWidth;
 encInitParam.height = inHeight;
 encInitParam.gopSize = 30/2;
 encInitParam.bitrate = bit_rate*1024;
 encInitParam.fpsNum = 30;
 encInitParam.fpsDen = 1;

 encInitParam.chromaInterleave = 0;

 // Rate Control
 encInitParam.enableRC = 1;  // Enable Rate Control
 encInitParam.disableSkip = 0; // Enable Skip
 encInitParam.maximumQp = 51; // Max Qunatization Scale
 encInitParam.initialQp = 10; // Default Encoder API ( enableRC == 0 )
 encInitParam.enableAUDelimiter = 1; // Enable / Disable AU Delimiter

    NX_VID_MEMORY_INFO *hInImage = NULL;
    hInImage = NX_VideoAllocateMemory( 16, inWidth, inHeight, NX_MEM_MAP_LINEAR, FOURCC_MVS0 );

   
 ret = NX_VidEncInit( hEnc, &encInitParam );
 if(ret != 0)
    {
        printf("NX_VidEncInit=%d\n", ret);
        goto end;
    }  
   
   
    int size;
    NX_VidEncGetSeqInfo( hEnc, seqBuffer, &size ); //SPS + PPS
    /*
    00000001 6742401e a680a03d 90000000  0168ce38 80
    SPS: 9字节 67 42 40 1e a6 80 a0 3d 90
    PPS: 4字节 68 ce 38 80
    */
   
 if(fp_h264 != NULL) fwrite( seqBuffer, 1, size, fp_h264 );
   
    ret = decode_sps_pps(&g_metaData, seqBuffer, size);

    ret=SendVideoSpsPps(g_metaData.Pps,g_metaData.nPpsLen,g_metaData.Sps,g_metaData.nSpsLen);


    AVFrame *pFrame,*pFrameYUV;
    pFrame=avcodec_alloc_frame();
    pFrameYUV=avcodec_alloc_frame();
    uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
    avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);

    AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
    printf("File Information---------------------\n");
    av_dump_format(ifmt_ctx,0,NULL,0);
    printf("-------------------------------------------------\n");

    struct SwsContext *img_convert_ctx;
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

    int got_picture;
    AVPacket packetOut;
    unsigned int tick = 0; 
 unsigned int tick_gap = 30;
    while(!g_nStop && av_read_frame(ifmt_ctx, packet)>=0){
     if(packet->stream_index == videoindex){
  ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
  if(ret <0)
  {
      printf("Decode Error.\n");
  }
  if(got_picture)
  {
   sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
   int y_size = pCodecCtx->width*pCodecCtx->height;
   if(fp_yuv != NULL)
      {
          fwrite(pFrameYUV->data[0],1, y_size, fp_yuv); //Y
       fwrite(pFrameYUV->data[1],1, y_size/4, fp_yuv); //U
       fwrite(pFrameYUV->data[2],1, y_size/4, fp_yuv); //V
      }
   memcpy((unsigned char*)hInImage->luVirAddr, pFrameYUV->data[0], y_size);
   memcpy((unsigned char*)hInImage->cbVirAddr, pFrameYUV->data[1], y_size/4);
   memcpy((unsigned char*)hInImage->crVirAddr, pFrameYUV->data[2], y_size/4);
   
   encIn.pImage = hInImage;
            encIn.timeStamp = packet->pts;
            encIn.forcedIFrame = 0;
            encIn.forcedSkipFrame = 0;
            encIn.quantParam = 25;
           
            NX_VidEncEncodeFrame( hEnc, &encIn, &encOut );
            if(encOut.bufSize>0 )
   {
             /*发送*/
             //ret = SendH264Packet(encOut.outBuf, encOut.bufSize, encOut.frameType == PIC_TYPE_I, packet->pts);
                ret = SendH264Packet(encOut.outBuf,encOut.bufSize, encOut.frameType == PIC_TYPE_I, tick);
                tick +=tick_gap;
       //printf("encOut.frameType:%d, encOut.bufSize:%ld\n", encOut.frameType, encOut.bufSize);
    if(fp_h264 != NULL) fwrite( encOut.outBuf, 1, encOut.bufSize, fp_h264 );
    //dumpdata( encOut.outBuf, 16, "" );
    totalSize += encOut.bufSize;
    bitRate = (double)totalSize/(double)frameCnt*.8;
    //printf("bitRate = %4.3f kbps\n", bitRate*30/1024.);
   }
   frameCnt ++;
  }
     }
     av_free_packet(packet);
    }
    if( hInImage != NULL )
 {
  NX_FreeVideoMemory( hInImage );
 }
    sws_freeContext(img_convert_ctx);
   
    if(fp_yuv != NULL) fclose(fp_yuv);
    if(fp_h264 != NULL) fclose(fp_h264);

end:
    free(g_metaData.Sps);
 free(g_metaData.Pps);
 
    NX_VidEncClose( hEnc );
    av_free(out_buffer);
    av_free(pFrameYUV);
    avcodec_close(pCodecCtx);
    avformat_close_input(&ifmt_ctx);
    return 0;
}


你可能感兴趣的:(嵌入式设备上ffmpeg取摄像头+硬编码+rtmp推送)