代码拉取完成,页面将自动刷新
#include "base.h"
#include "flv.h"
#include "func.h"
#include "hylog.h"
#define AUDIO_AAC 0
//union voidint{void*p;int i;};
#include <event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include <event2/dns.h>
#include <event2/thread.h>
#include <evhttp.h>
#include "flv.h"
#include "func.h"
#include "H264ParseSPS.h"
static void send_metadata(struct flv_t *flv);
// 填充FLV文件头
static uint32_t flv_fillFLVH(uint8_t *pdata, uint8_t have_audio)
{
const uint8_t flv_header[13] = {0x46, 0x4c, 0x56, 0x01, 0x01, 0x00, 0x00, 0x00, 0x09, \
0x00, 0x00,0x00,0x00};
/*
const uint8_t flv_header_2[13] = {0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 0x09, \
0x00, 0x00,0x00,0x00};
*/
memcpy(pdata, flv_header, 13);
if(have_audio)
pdata[4] |= 4;
/*
memcpy(pdata, flv_header_2, 13);
else
memcpy(pdata, flv_header_1, 13);
*/
return 13;
}
// 填充FLV的tag头
static uint32_t flv_fillTH( uint8_t **pdata, uint8_t tag_type, uint32_t size, uint32_t ts )
{
uint8_t *pd;
pd = *pdata;
*pd++ = tag_type; // tag_type, 8 is audio, 9 is video
*pd++ = (uint8_t)((size >> 16)&0xff);
*pd++ = (uint8_t)((size >> 8)&0xff);
*pd++ = (uint8_t)((size >> 0)&0xff); // tag data size, exclude tag head
*pd++ = (uint8_t)((ts >> 16)&0xff);
*pd++ = (uint8_t)((ts >> 8)&0xff);
*pd++ = (uint8_t)((ts >> 0)&0xff); // ts, uint ms
*pd++ = (uint8_t)((ts >> 24)&0xff);
*pd++ = 0;
*pd++ = 0;
*pd++ = 0; // stream ID
*pdata = pd;
return 11;
}
// 填充FLV的video tag
static uint32_t flv_fillVT( uint8_t **pdata, uint8_t ftype, uint8_t nalu_c, uint8_t *nalu, uint32_t size )
{
uint8_t *pd;
pd = *pdata;
*pd++ = ftype;
*pd++ = nalu_c;
*pd++ = 0;
*pd++ = 0;
*pd++ = 0;
*pd++ = (uint8_t)((size>>24)&0xff);
*pd++ = (uint8_t)((size>>16)&0xff);
*pd++ = (uint8_t)((size>>8)&0xff);
*pd++ = (uint8_t)((size>>0)&0xff);
memcpy(pd, nalu, size);
pd = &pd[size];
size += 20;
*pd++ = (uint8_t)((size>>24)&0xff);
*pd++ = (uint8_t)((size>>16)&0xff);
*pd++ = (uint8_t)((size>>8)&0xff);
*pd++ = (uint8_t)((size>>0)&0xff);
*pdata = pd;
return size-11;
}
struct flv_t *open_flv(void *fd, enum FLVDST flvdst, struct metadata_t *meta, const char *ext_head)
{
struct flv_t *res;
uint8_t flv_header[13];
res = (struct flv_t *)mycalloc(sizeof(struct flv_t),1);
if(meta)
{
memcpy(&res->metadata, meta, sizeof(struct metadata_t));
}
else
{
res->metadata.m_duration=-1;
res->metadata.m_width=640;
res->metadata.m_height=480;
res->metadata.m_framerate=12.5;
}
res->fd=fd;
res->dst=flvdst;
res->rate = 25;
flv_fillFLVH(flv_header, res->metadata.m_hasautio!=0); // 有音频
if(flvdst==HTTP_FLV)
{
struct bufferevent *c=(struct bufferevent *)fd;
char buff[1024];
sprintf(buff,
"HTTP/1.1 200 OK\r\n"
"Transfer-Encoding: chunked\r\n"
"Content-Type: video/x-flv\r\n"
"Cache-Control: no-cache\r\n"
"Server: HYTerminal\r\n"
"Connection: keep-alive\r\n"
"Expires: -1\r\n"
"Access-Control-Allow-Credentials: true\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Headers: X-Requested-With\r\n"
"Access-Control-Allow-Methods: GET,POST,OPTIONS\r\n"
"%s"
"\r\n"
,ext_head==NULL?"":ext_head);
bufferevent_write(c,buff, strlen(buff));
bufferevent_write(c, "D\r\n", 3);
bufferevent_write(c, flv_header, 13);
bufferevent_write(c, "\r\n", 2);
send_metadata(res);
}
else
{
union voidint vi;
int dummy;
vi.p = fd;
if(lseek(vi.i, 0, SEEK_END)==0)
{
dummy = write(vi.i, flv_header, sizeof(flv_header));
send_metadata(res);
}
else
{
res->audio_ts=1;
// printf("exist file, just add data\n");
}
(void)dummy;
}
return res;
}
void close_flv(struct flv_t *flv)
{
myfree(flv);
}
static size_t find_nalu(uint8_t *pdata, size_t size, uint8_t **frame)
{
size_t ret=0;
size_t i;
for(i=0; i<size-5; i++)
{
if(memcmp(&pdata[i], "\0\0\0\1",4)==0)break; // 找到nalu单元
}
*frame=NULL;
if(i==size-5)
return ret; // 没找到nalu单元
*frame=&pdata[i+4];
ret = size-i-4;
return ret;
}
static void output(struct flv_t *flv, uint8_t *pdata, size_t size)
{
if(flv->dst==HTTP_FLV)
{
struct bufferevent *c=(struct bufferevent *)flv->fd;
char lstr[32];
size_t len;
sprintf(lstr, "%X\r\n", (int)size);
len = strlen(lstr);
bufferevent_write(c, lstr, len);
bufferevent_write(c, pdata, size);
bufferevent_write(c, "\r\n", 2);
}
else
{
union voidint vi;
vi.p = flv->fd;
write(vi.i, pdata, (unsigned int)size);
}
}
void send_video(struct flv_t *flv, uint8_t *pdata, size_t size)
{
uint8_t *pframe;
uint8_t *outp, *ptr;
size_t outl, off;
size_t flen;
if(pdata==NULL)return; // 缓冲区不能为空
if(flv==NULL)return; // 句柄不能为空
if(size<5)return; // 帧长不够
flen=find_nalu(pdata, size, &pframe);
if(flen==0)return;
if(pframe[0]==0x67) // PPS
{
size_t flen1, flen2;
sps_info_struct info;
uint8_t *pframe1, *pframe2;
flen1 = find_nalu(pframe, flen, &pframe1);
if(flen1==0)return; // 不是SPS
flen2 = find_nalu(pframe1, flen1, &pframe2);
if(flen < flen1+4)return;
flen -= flen1+4;
if(h264_parse_sps(pframe, (unsigned int)flen, &info))
{
flv->rate = info.fps;
}
if(flen1 < flen2+4)return;
flen1 -= flen2+4;
outl = flen+flen1+16;
outp = (uint8_t*)myalloc(outl+15);
ptr = outp;
// flv tag header
off = flv_fillTH(&ptr, 9, (uint32_t)outl, flv->video_ts);
// flv video tag header
outp[off++] = 0x17; //key frame, AVC
outp[off++] = 0x00; //avc sequence header
outp[off++] = 0x00; // composit time
outp[off++] = 0x00; // composit time
outp[off++] = 0x00; // composit time
// flv VideoTagBody --AVCDecoderCOnfigurationRecord
outp[off++] = 0x01; // configurationversion
outp[off++] = (uint8_t)(pframe[1]); // avcprofileindication
outp[off++] = (uint8_t)(pframe[2]); // profilecompatibilty
outp[off++] = (uint8_t)(pframe[3]); // avclevelindication
outp[off++] = 0xff; // reserved + lengthsizeminusone
outp[off++] = 0xe1; // numofsequenceset
outp[off++] = (uint8_t)((flen>>8)&0xff);
outp[off++] = (uint8_t)((flen>>0)&0xff);
memcpy(&outp[off], pframe, flen);
off += flen;
outp[off++] = 0x01; // numofpictureset
outp[off++] = (uint8_t)((flen1>>8)&0xff);
outp[off++] = (uint8_t)((flen1>>0)&0xff);
memcpy(&outp[off], pframe1, flen1);
off += flen1;
outl += 11;
outp[off++] = (uint8_t)((outl>>24)&0xff);
outp[off++] = (uint8_t)((outl>>16)&0xff);
outp[off++] = (uint8_t)((outl>>8)&0xff);
outp[off++] = (uint8_t)((outl>>0)&0xff);
output(flv, outp, outl+4);
myfree(outp);
if(flen2==0)return; // 关键帧
send_video(flv, pframe2, flen2);
}
else if(pframe[0]==0x06)
{
send_video(flv, pframe, flen);
}
else if(pframe[0]==0x65 || pframe[0]==0x61)
{
flv->video_ts += (uint32_t)(1000/flv->rate);
outl = flen+9;
outp = (uint8_t*)myalloc(outl+15);
ptr = outp;
// flv tag header
off = flv_fillTH(&ptr, 9, (uint32_t)outl, flv->video_ts);
flv_fillVT(&ptr, pframe[0]==0x61?0x27:0x17, 0x01, pframe, (uint32_t)flen);
output(flv, outp, outl+15);
myfree(outp);
}
}
void send_audio(struct flv_t *flv, uint8_t *pdata, size_t size)
{
uint8_t *outp, *ptr;
size_t outl;
if(pdata==NULL)return; // 缓冲区不能为空
if(flv==NULL)return; // 句柄不能为空
if(size<4)return; // 帧长不够
if(AUDIO_AAC)
{
outl = size;
if(flv->audio_ts==0) // 属于AAC编码,开始写AAC头
{
uint16_t type;
outp = (uint8_t*)myalloc(19);
ptr = outp;
flv_fillTH(&ptr, 8, 4, flv->audio_ts);
*ptr++ = 0xAE;
*ptr++ = 0x00;
type = 2<<11;
type |= 4<<7;
type |= 1<<3;
*ptr++ = type&0xff;
*ptr++ = (type>>8)&0xff;
*ptr++ = 0;
*ptr++ = 0;
*ptr++ = 0;
*ptr++ = 15;
output(flv, outp, 19);
myfree(outp);
}
outp = (uint8_t*)myalloc(outl+17); // TAG_HEADER:11 bytes, PRE_TAG_LEN:4, AUDIO_TYPE:1 byte, AAC_CONF:1 byte, total 17 bytes
ptr = outp;
flv_fillTH(&ptr, 8, outl+2, flv->audio_ts); // TAG_HEADER, 11 bytes, body_len, body+audio type+aac conf
}
else
{
outl = (uint8_t)pdata[2];
outl *= 2;
if(outl+4 != size)return; // 帧长不对
outp = (uint8_t*)myalloc(outl+16);
ptr = outp;
flv_fillTH(&ptr, 8, outl+1, flv->audio_ts);
}
flv->audio_ts += 64; // 320点/帧,8k采样率
if(AUDIO_AAC)
{
*ptr++ = 0xAE; // audio type
*ptr++ = 0x01; // aac conf
memcpy(ptr, pdata, outl);
ptr = &ptr[outl];
outl += 13;
}
else
{
*ptr++ = 0x1E; // ADPCM
memcpy(ptr, &pdata[4], outl);
ptr = &ptr[outl];
outl += 12;
}
*ptr++ = (uint8_t)((outl>>24)&0xff);
*ptr++ = (uint8_t)((outl>>16)&0xff);
*ptr++ = (uint8_t)((outl>>8)&0xff);
*ptr++ = (uint8_t)((outl>>0)&0xff);
output(flv, outp, outl+4);
myfree(outp);
}
void m_fill_double(int *off, uint8_t *dst, double val)
{
uint8_t *ptr;
int _off=*off;
int i;
ptr = (uint8_t*)&val;
dst[_off++] = 0;
for(i=0; i<8; i++)
{
dst[_off+i] = ptr[7-i];
}
*off = _off+8;
}
void m_fill_string(int *off, uint8_t *dst, char*val)
{
int _off=*off;
size_t len=strlen(val);
dst[_off++] = 2;
dst[_off++] = (len>>16)&0xff;
dst[_off++] = (len>>0)&0xff;
memcpy(&dst[_off], val,len);
*off = (int)(len)+_off;
}
void m_fill_bool(int *off, uint8_t *dst, int val)
{
int _off=*off;
dst[_off++] = 1;
dst[_off++] = val!=0;
*off = _off;
}
void m_fill_key(int *off, uint8_t *dst, char*val)
{
int _off;
size_t len=strlen(val);
_off = *off;
dst[_off++] = (len>>16)&0xff;
dst[_off++] = (len>>0)&0xff;
memcpy(&dst[_off], val,len);
*off = (int)(len)+_off;
}
void send_metadata(struct flv_t *flv)
{
int off, dsize;
char buff[64];
char *p;
uint8_t *ptr;
uint8_t *mbuff=(uint8_t*)mycalloc(512,1);
off=0;
ptr = mbuff;
ptr[off++] = 0x12; // type is metadata
off += 3; // metadata size
off += 4; // metadata time stamp
off += 3; // stream id
ptr[off++] = 0x02; // AMF1 type is 2(string)
ptr[off++] = 0x00;
ptr[off++] = 0x0A; // AMF1 string length
memcpy(&ptr[off], "onMetaData", 10); // AMF1 string "onMetaData"
off += 10;
ptr[off++] = 0x08; // AMF2 type is array
ptr[off++] = 0;
ptr[off++] = 0;
ptr[off++] = 0;
ptr[off++] = 0x0d; // AMF2 array size is 13
m_fill_key(&off, ptr, "duration");
m_fill_double(&off, ptr, flv->metadata.m_duration);
m_fill_key(&off, ptr, "width");
m_fill_double(&off, ptr, flv->metadata.m_width);
m_fill_key(&off, ptr, "height");
m_fill_double(&off, ptr, flv->metadata.m_height);
m_fill_key(&off, ptr, "framerate");
m_fill_double(&off, ptr, flv->metadata.m_framerate);
m_fill_key(&off, ptr, "videodatarate");
m_fill_double(&off, ptr, flv->metadata.m_videodatarate);
m_fill_key(&off, ptr, "audiodatarate");
m_fill_double(&off, ptr, flv->metadata.m_audiodatarate);
m_fill_key(&off, ptr, "videocodecid");
m_fill_double(&off, ptr, flv->metadata.m_videocodecid);
m_fill_key(&off, ptr, "audiocodecid");
m_fill_double(&off, ptr, flv->metadata.m_audiocodecid);
m_fill_key(&off, ptr, "audiosamplerate");
m_fill_double(&off, ptr, flv->metadata.m_audiosamplerate);
m_fill_key(&off, ptr, "audiosamplesize");
m_fill_double(&off, ptr, flv->metadata.m_audiosamplesize);
m_fill_key(&off, ptr, "stereo");
m_fill_bool(&off, ptr, flv->metadata.m_stereo);
m_fill_key(&off, ptr, "title");
m_fill_string(&off, ptr, "HangYi TECH video");
m_fill_key(&off, ptr, "metadatacreator");
m_fill_string(&off, ptr, "HangYi TECH ship Terminal, at Aug 2022");
sprintf(buff, "flv-video-chn%d", flv->metadata.m_chn+1);
m_fill_key(&off, ptr, "comment");
m_fill_string(&off, ptr, buff);
m_fill_key(&off, ptr, "CanSeekToEnd");
m_fill_bool(&off, ptr, 0);
p = localtimet2str(time(NULL));
m_fill_key(&off, ptr, "metadatadate");
m_fill_string(&off, ptr, p);
myfree(p);
ptr[off++] = 0;
ptr[off++] = 0;
ptr[off++] = 9;
dsize = off-11;
ptr[1] = (dsize>>16)&0xff;
ptr[2] = (dsize>>8)&0xff;
ptr[3] = (dsize)&0xff;
dsize=off;
ptr[off++] = (dsize>>24)&0xff;
ptr[off++] = (dsize>>16)&0xff;
ptr[off++] = (dsize>>8)&0xff;
ptr[off++] = (dsize>>0)&0xff;
output(flv, ptr, off);
myfree(ptr);
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。