Chinaunix
标题:
linux下面如何用ffmpeg将h264帧数据村存为FLV文件!!
[打印本页]
作者:
wansbest
时间:
2011-10-19 10:08
标题:
linux下面如何用ffmpeg将h264帧数据村存为FLV文件!!
最近一段时间一直在研究怎么将接收到的H264帧数据,存为一个FLV文件,初步的想法是用ffmpeg提供的开源库来实现,但因为之前一直没接触过这个,具体怎么做还是一筹莫展,各位前辈有没有之前做过这个
的,提供一些经验呗!!!
或者不用ffmpeg,有别的方案也行,但在转换效率以及稳定性上要求很高,因为这是服务器上要做的功能,而且数据量会特别庞大。。。。。
作者:
zylthinking
时间:
2011-10-19 10:18
靠, 这个我作过, FLV 的文件格式非常简单, 直接找 adobe 的说明, 照着写就行了
作者:
wansbest
时间:
2011-10-19 10:52
是不是直接就拿H264的帧往flv的tag里面放,然后那些header自己根据帧的信息去构造啊!!照这么说,ffmpeg岂不根本用不到了啊!!!
作者:
hniu
时间:
2011-10-19 10:52
授 lz 以鱼
http://wenku.baidu.com/view/f919978102d276a200292e2f.html
作者:
efolzl
时间:
2011-10-19 10:56
一个是压缩算法,一个是存储格式。你要是只保持成文件,按格式存就是了。
不解码的话,应该不涉及ffmpeg。
作者:
zylthinking
时间:
2011-10-19 11:07
本帖最后由 zylthinking 于 2011-10-19 11:08 编辑
那我也不卖渔了, 摘些代码
static char* g_lastKeyFrame = NULL;
static int g_keyfrm_buf = 0;
static int g_keyfrm_len = 0;
int FlvSaveKeyFrame(void* data, int len){
if(len > g_keyfrm_buf){
free(g_lastKeyFrame);
g_lastKeyFrame = (char *) malloc(len);
if(g_lastKeyFrame == NULL){
g_keyfrm_len = 0;
return -1;
}
g_keyfrm_buf = len;
}
g_keyfrm_len = len;
if(len > 0){
memcpy(g_lastKeyFrame, data, g_keyfrm_len);
}
return 0;
}
int FlvGetLastKeyFrame(char*& buf){
buf = g_lastKeyFrame;
return g_keyfrm_len;
}
static char* g_sps_buf = NULL;
static int g_sps_buf_len = 0;
static int g_sps_len = 0;
int FlvSaveSpsFrame(void* data, int len){
if(len > g_sps_buf_len){
free(g_sps_buf);
g_sps_buf = (char *) malloc(len);
if(g_sps_buf == NULL){
g_sps_buf_len = 0;
return -1;
}
g_sps_buf_len = len;
}
g_sps_len = len;
if(len > 0){
memcpy(g_sps_buf, data, g_sps_len);
}
return 0;
}
int FlvGetSpsFrame(char*& buf){
buf = g_sps_buf;
return g_sps_len;
}
static char* g_aac_buf = NULL;
static int g_aac_buf_len = 0;
static int g_aac_len = 0;
int FlvSaveAacFrame(void* data, int len){
if(len > g_aac_buf_len){
free(g_aac_buf);
g_aac_buf = (char *) malloc(len);
if(g_aac_buf == NULL){
g_aac_buf_len = 0;
return -1;
}
g_aac_buf_len = len;
}
g_aac_len = len;
if(len > 0){
memcpy(g_aac_buf, data, g_aac_len);
}
return 0;
}
int FlvGetAacFrame(char*& buf){
buf = g_aac_buf;
return g_aac_len;
}
typedef int (*type_parser)(const char* flv_buffer, media_info& info);
struct flv_media_parser{
DWORD fourcc;
type_parser parser;
};
//=========================================================
// stupid compiler
static void my_free(void* addr){
free(addr);
}
static int h264_parser(const char* flv_buffer, media_info& info){
info.bit_count = 0;
info.extra = 1;
int byte1 = ((int) flv_buffer[1]) & 0xff;
int byte2 = ((int) flv_buffer[2]) & 0xff;
int byte3 = ((int) flv_buffer[3]) & 0xff;
DWORD len = (byte1 << 16) | (byte2 << 8) | byte3;
int byte4 = ((int) flv_buffer[4]) & 0xff;
int byte5 = ((int) flv_buffer[5]) & 0xff;
int byte6 = ((int) flv_buffer[6]) & 0xff;
int byte7 = ((int) flv_buffer[7]) & 0xff;
int byte13 = ((int) flv_buffer[13]) & 0xff;
int byte14 = ((int) flv_buffer[14]) & 0xff;
int byte15 = ((int) flv_buffer[15]) & 0xff;
info.dts = (byte7 << 24) | (byte4 << 16) | (byte5 << 8) | byte6;
int offset = (byte13 << 16) | (byte14 << 8) | byte15;
info.pts = info.dts + offset;
int byte12 = ((int) flv_buffer[12]) & 0xff;
if(byte12 == 0){
int sps_len = 0;
int pps_len = 0;
const char* sps = NULL;
const char* pps = NULL;
int nr_sps = ((int) flv_buffer[21]) & 0x1f;
if(nr_sps == 1){
int byte22 = ((int) flv_buffer[22]) & 0xff;
int byte23 = ((int) flv_buffer[23]) & 0xff;
sps_len = (byte22 << 8) | byte23;
sps = &flv_buffer[24];
pps = sps + sps_len;
sps_len += 4;
}else{
// no more than 1 sps currently
assert(nr_sps == 0);
}
int nr_pps = pps[0] & 0xff;
if(nr_pps == 1){
int byte1 = ((int) pps[1]) & 0xff;
int byte2 = ((int) pps[2]) & 0xff;
pps_len = ((byte1 << 8) | byte2) + 4;
pps = &pps[3];
}else{
pps = NULL;
// no more than 1 pps currently
assert(nr_pps == 0);
}
int header_len = sps_len + pps_len;
assert(header_len > 0);
unsigned char* buffer = (unsigned char *) malloc(header_len);
if(buffer == NULL){
return -1;
}
info.free = my_free;
info.length = header_len;
info.buffer = (char *) buffer;
if(sps_len > 0){
sps_len -= 4;
buffer[0] = sps_len >> 24;
buffer[1] = sps_len >> 16;
buffer[2] = sps_len >> 8;
buffer[3] = sps_len;
buffer += 4;
memcpy(buffer, sps, sps_len);
buffer += sps_len;
}
if(pps_len > 0){
pps_len -= 4;
buffer[0] = pps_len >> 24;
buffer[1] = pps_len >> 16;
buffer[2] = pps_len >> 8;
buffer[3] = pps_len;
buffer += 4;
memcpy(buffer, pps, pps_len);
buffer += pps_len;
}
return len + 11 + 4;
}else if(byte12 == 1){
info.length = len - 5;
info.buffer = (char *) &flv_buffer[16];
return len + 11 + 4;
}
return -1;
}
static flv_media_parser flv_h264_parser = {
make_fourcc('H', '2', '6', '4'),
h264_parser
};
static flv_media_parser* video_parser_of(char codec_id){
if(codec_id == (char) 0x07){
return &flv_h264_parser;
}
return NULL;
}
static int flv_video_unpack(const char* flv_buffer, media_info& info){
char byte11 = flv_buffer[11] & 0xff;
BOOL key_frame = (0x10 == (byte11 & 0xf0));
int codec_id = (byte11 & 0x0f);
flv_media_parser* parser = video_parser_of(codec_id);
if(parser == NULL){
return -1;
}
info.fourcc = parser->fourcc;
return parser->parser(flv_buffer, info);
}
//=========================================================
static char adts[7] = {0xff, 0xf1};
static int aac_parser(const char* flv_buffer, media_info& info){
int byte1 = ((int) flv_buffer[1]) & 0xff;
int byte2 = ((int) flv_buffer[2]) & 0xff;
int byte3 = ((int) flv_buffer[3]) & 0xff;
DWORD len = (byte1 << 16) | (byte2 << 8) | byte3;
int byte4 = ((int) flv_buffer[4]) & 0xff;
int byte5 = ((int) flv_buffer[5]) & 0xff;
int byte6 = ((int) flv_buffer[6]) & 0xff;
int byte7 = ((int) flv_buffer[7]) & 0xff;
info.dts = (byte7 << 24) | (byte4 << 16) | (byte5 << 8) | byte6;
info.pts = info.dts;
// flash only support 44k, 16bits aac
info.sample_rate = 44100;
info.bit_count = 16;
info.chans = (flv_buffer[11] & 0x01) ? 2 : 1;
int type = ((int) flv_buffer[12]) & 0xff;
if(type == 0){
int byte13 = ((int) flv_buffer[13]) & 0xff;
int objType = ((byte13 & 0xf1) >> 3) - 1;
int byte14 = ((int) flv_buffer[14]) & 0xff;
int samplerate_indx = ((byte13 & 0x7) << 1 | (byte14 & 0x80) >> 7);
int chans = (byte14 & 0x71) >> 3;
// format of flash
assert(chans == 2 && samplerate_indx == 7);
adts[2] = (objType << 6) | (samplerate_indx << 2) | ((chans & 0x04) >> 2);
adts[3] = ((chans & 0x03) << 6);
return len + 11 + 4;
}else if(type == 1){
if(adts[2] == 0){
cs << "drop audio data, because of header is missing" << endl;
goto LABEL;
}
const static int adts_len = sizeof(adts);
info.length = len - 2 + adts_len;
info.buffer = (char *) malloc(info.length);
if(info.buffer == NULL){
return -1;
}
info.free = my_free;
assert(sizeof(int) == 4);
*((int *) &info.buffer[0]) = *((int *) &adts[0]);
info.buffer[3] |= (info.length & 0x1fff) >> 11;
info.buffer[4] = (info.length & 0x7ff) >> 3;
info.buffer[5] = (info.length & 0x07) << 5;
info.buffer[5] |= 0x1f;
info.buffer[6] = 0xfc;
memcpy((void *) &info.buffer[adts_len], &flv_buffer[13], len - 2);
LABEL:
return len + 11 + 4;
}
return -1;
}
static flv_media_parser flv_aac_parser = {
mpeg4_aac,
aac_parser
};
static flv_media_parser* audio_parser_of(char codec_id){
if(codec_id == (char) 0xa0){
return &flv_aac_parser;
}
return NULL;
}
static int flv_audio_unpack(const char* flv_buffer, media_info& info){
char byte11 = flv_buffer[11] & 0xff;
char fmt = byte11 & 0xf0;
flv_media_parser* parser = audio_parser_of(fmt);
if(parser == NULL){
return -1;
}
info.fourcc = parser->fourcc;
return parser->parser(flv_buffer, info);
}
//=========================================================
int FlvUnpack(const char* flv_buffer, media_info& info){
info.free = NULL;
info.buffer = NULL;
info.length = 0;
int n = -1;
char audio_or_video = flv_buffer[0];
if(audio_or_video == 0x09){
n = flv_video_unpack(flv_buffer, info);
if(n != -1){
if(FlvIsSpsFrame((BYTE *) flv_buffer, n)){
FlvSaveSpsFrame((void *) flv_buffer, n);
}else if(FlvIsKeyFrame((BYTE *) flv_buffer, n)){
FlvSaveKeyFrame((void *) flv_buffer, n);
}
}
}
if(audio_or_video == 0x08){
n = flv_audio_unpack(flv_buffer,info);
if(n != -1){
if(FlvIsAacHeaderFrame((BYTE *) flv_buffer, n)){
FlvSaveAacFrame((void *) flv_buffer, n);
}
}
}
return n;
}
BOOL FlvIsKeyFrame(BYTE* flv_buffer, int len){
if(len < 12 || flv_buffer[0] != 0x09){
return FALSE;
}
return (0x10 == (flv_buffer[11] & 0xf0));
}
BOOL FlvIsSpsFrame(BYTE* flv_buffer, int len){
if(len < 13 || flv_buffer[0] != 0x09){
return FALSE;
}
return (0 == (flv_buffer[12]));
}
BOOL FlvIsAacHeaderFrame(BYTE* flv_buffer, int len){
if(len < 13 || flv_buffer[0] != 0x08){
return FALSE;
}
return (0 == (flv_buffer[12]));
}
int FlvFrameLength(BYTE* flv_buffer, int len){
if(len < 4){
return -1;
}
int byte1 = ((int) flv_buffer[1]) & 0xff;
int byte2 = ((int) flv_buffer[2]) & 0xff;
int byte3 = ((int) flv_buffer[3]) & 0xff;
int length = (byte1 << 16) | (byte2 << 8) | byte3;
length += 11 + 4;
if(length <= len){
return length;
}
return -1;
}
DWORD FlvGetDts(BYTE* flv_buffer, int len){
if(len < 8){
return (DWORD) (-1);
}
int byte4 = ((int) flv_buffer[4]) & 0xff;
int byte5 = ((int) flv_buffer[5]) & 0xff;
int byte6 = ((int) flv_buffer[6]) & 0xff;
int byte7 = ((int) flv_buffer[7]) & 0xff;
DWORD st = (byte7 << 24) | (byte4 << 16) | (byte5 << 8) | byte6;
return st;
}
void FlvSetDts(UINT32 ts, void* buf, int len, BOOL zeroOffset){
int left = len;
char* pch = (char *) buf;
while(left > 0){
int n1 = pch[1] & 0xff;
int n2 = pch[2] & 0xff;
int n3 = pch[3] & 0xff;
int n = (n1 << 16) | (n2 << 8) | n3;
*((char*)pch + 4) = (unsigned char) (ts >> 16);
*((char*)pch + 5) = (unsigned char) (ts >> 8);
*((char*)pch + 6) = (unsigned char) ts;
*((char*)pch + 7) = (unsigned char) (ts >> 24);
if(zeroOffset){
*((char*)pch + 13) = 0;
*((char*)pch + 14) = 0;
*((char*)pch + 15) = 0;
}
pch += (n + 11 + 4);
left -= (n + 11 + 4);
}
assert(left == 0);
}
int FlvMediaType(const char* flv_buffer, int len){
if(len < 1){
goto LABEL;
}
if(flv_buffer[0] == 0x09){
return media_video;
}else if(flv_buffer[0] == 0x08){
return media_audio;
}
LABEL:
assert(false);
return media_none;
}
int FlvTagBytes(int* head, int* tail){
if(head) *head = 11;
if(tail) *tail = 4;
return 15;
}
const static BYTE hdrflvout[] = {
0x46,0x4C,0x56,0x01,0x05,0x00,0x00,0x00,0x09,
0x00,0x00,0x00,0x00,
};
CFlvWrite::CFlvWrite(void){
m_bOk = FALSE;
m_hFile = NULL;
m_need_sps = TRUE;
m_need_kfrm = TRUE;
m_need_aach = TRUE;
m_base_tick_video = 0;
m_base_tick_audio = 0;
}
CFlvWrite::~CFlvWrite(void){
Close();
}
HRESULT CFlvWrite::Open(LPCTSTR szOutName, LPCTSTR szExtParam){
if(m_bOk)
return S_OK; //已经打开
m_bOk = FALSE;
m_hFile = _tfopen(szOutName, _T("wb"));
if( m_hFile <= 0)
return E_FAIL;
fwrite(hdrflvout, sizeof(hdrflvout), 1, m_hFile);
m_bOk = TRUE;
return S_OK;
}
HRESULT CFlvWrite::Write(BYTE* ppData, int nLen){
SingleLock l(m_cs);
if(!m_bOk){
return E_FAIL;
}
if(m_hFile == NULL){
ASSERT(FALSE);
return E_FAIL;
}
int sps_len = 0;
char* sps_buf;
if(m_need_sps){
sps_len = FlvGetSpsFrame(sps_buf);
}
int keyframe_len = 0;
char* kfrm_buf;
if(m_need_kfrm){
keyframe_len = FlvGetLastKeyFrame(kfrm_buf);
}
int aac_len = 0;
char* aac_buf;
if(m_need_aach){
aac_len = FlvGetAacFrame(aac_buf);
}
while(nLen > 0){
int len = FlvFrameLength(ppData, nLen);
ASSERT(len != -1);
int type = FlvMediaType((const char *) ppData, len);
ASSERT(type != media_none);
if(type == media_audio){
if(m_need_aach){
BOOL aach = FlvIsAacHeaderFrame(ppData, len);
if(aach){
m_need_aach = FALSE;
FlvSetDts(0, ppData, len, FALSE);
fwrite(ppData, 1, len, m_hFile);
goto LABEL;
}else if(aac_len){
FlvSetDts(0, aac_buf, aac_len, FALSE);
fwrite(aac_buf, 1, aac_len, m_hFile);
m_need_aach = FALSE;
}else{
goto LABEL;
}
}
ASSERT(!m_need_aach);
DWORD ts = FlvGetDts(ppData, len);
if(m_base_tick_audio == 0){
m_base_tick_audio = ts;
}
FlvSetDts(ts - m_base_tick_audio, ppData, len, FALSE);
fwrite(ppData, 1, len, m_hFile);
}else{
BOOL sps = FlvIsSpsFrame(ppData, len);
if(sps){
FlvSetDts(0, ppData, len, TRUE);
fwrite(ppData, 1, len, m_hFile);
m_need_sps = FALSE;
goto LABEL;
}
if(m_need_sps){
if(sps_len == 0){
goto LABEL;
}
FlvSetDts(0, sps_buf, sps_len, TRUE);
fwrite(sps_buf, 1, sps_len, m_hFile);
m_need_sps = FALSE;
}
DWORD ts = FlvGetDts(ppData, len);
if(m_base_tick_video == 0){
m_base_tick_video = ts;
}
if(FlvIsKeyFrame(ppData, len)){
m_need_kfrm = FALSE;
}else if(m_need_kfrm){
if(keyframe_len == 0){
goto LABEL;
}
FlvSetDts(0, kfrm_buf, keyframe_len, TRUE);
fwrite(kfrm_buf, 1, keyframe_len, m_hFile);
m_need_kfrm = FALSE;
}
FlvSetDts(ts - m_base_tick_video, ppData, len, FALSE);
fwrite(ppData, 1, len, m_hFile);
}
LABEL:
ppData += len;
nLen -= len;
}
return S_OK;
}
HRESULT CFlvWrite::Close(){
if(m_hFile){
fclose(m_hFile);
}
m_base_tick_video = m_base_tick_audio = 0;
m_need_aach = m_need_sps = m_need_kfrm = TRUE;
m_hFile = NULL;
m_bOk = FALSE;
return S_OK;
}
复制代码
作者:
AD8018
时间:
2011-10-19 11:12
貌似能实现无限长度的视频会议!Great!我需要!
作者:
wansbest
时间:
2011-10-19 11:51
感谢各位,我们公司就是做网络会议的,不过我正在做的是会后的一个附加功能,提供会议的录制,要求对于会
中当中的视频数据,音频数据等。最后全部整合成一个文件,支持会后回放。我也就是一个新手,今年刚毕业
的,在这边也基本上给前辈打打下手,以后不懂得地方还得请各位多多关照!!!
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2