📄 flvdecoder.cpp
字号:
#include <sys/stat.h>
#include "FLVDecoder.h"
TFLVGlobalVar flvGlobalVar;
int filesize(CNeoFILE* aNeoFile)
{
return aNeoFile->FSize();
}
int filesize_from_current(CNeoFILE* aNeoFile)
{
return filesize(aNeoFile) - aNeoFile->FTell();
}
int flv_read_header(CNeoFILE* aNeoFile, unsigned char& flags)
{
unsigned char buff[4];
unsigned int offset;
int fsize = filesize(aNeoFile);
int pos = aNeoFile->FTell();
if (fsize < 4+1+4)
return 1;
aNeoFile->FRead((char*)buff, 4);
if (buff[0] != 'F' || buff[1] != 'L' || buff[2] != 'V')
return -1;
aNeoFile->FRead((char*)buff, 1);
offset = get_be32(aNeoFile);
if (fsize < offset)
{
aNeoFile->FSeek(pos, SEEK_SET);
return 1;
}
flags = buff[0];
aNeoFile->FSeek(offset, SEEK_SET);
return 0;
}
int flv_read_next_packet(CNeoFILE* aNeoFile, unsigned char* &buff, int& size, int& pts, int& retType, int buff_null)
{
int type, flags, next, pos;
pos = aNeoFile->FTell();
aNeoFile->FSeek(4, SEEK_CUR);
{
unsigned char val[7];
aNeoFile->FRead((char*)&val, 7);
type = val[0];
size = (val[1] << 16) | (val[2] << 8) | (val[3]);
pts = (val[4] << 16) | (val[5] << 8) | (val[6]);
}
if (aNeoFile->FEof())
{
aNeoFile->FSeek(pos, SEEK_SET);
return -1;
}
if (filesize_from_current(aNeoFile) < 4)
{
aNeoFile->FSeek(pos, SEEK_SET);
return -1;
}
aNeoFile->FSeek(4, SEEK_CUR);
flags = 0;
retType = 0;
if (size == 0)
return 0;
next = size + aNeoFile->FTell();
if (filesize_from_current(aNeoFile) < size)
{
aNeoFile->FSeek(pos, SEEK_SET);
return -1;
}
if (type == FLV_TAG_TYPE_AUDIO) {
flags = get_byte(aNeoFile);
if (buff_null)
buff = (unsigned char*)malloc(size);
aNeoFile->FRead((char*)buff, size-1);
if (aNeoFile->FTell() != next)
aNeoFile->FSeek(next, SEEK_SET);
retType = FLV_TAG_TYPE_AUDIO;
return 0;
} else if (type == FLV_TAG_TYPE_VIDEO) {
flags = get_byte(aNeoFile);
if (buff_null)
buff = (unsigned char*)malloc(size);
aNeoFile->FRead((char*)buff, size-1);
if (aNeoFile->FTell() != next)
aNeoFile->FSeek(next, SEEK_SET);
retType = FLV_TAG_TYPE_VIDEO;
if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)
AddKeyFrame(pts, pos);
return 0;
} else if (type == FLV_TAG_TYPE_META) {
if (size > 13+1+4)
flv_read_metabody(aNeoFile, next);
else /* skip packet */
;
if (aNeoFile->FTell() != next)
aNeoFile->FSeek(next, SEEK_SET);
return 0;
} else {
return 0;
}
}
int flv_read_next_packet_header(CNeoFILE* aNeoFile, int& retType, long& next, int& pts)
{
int type, pos, size;
pos = aNeoFile->FTell();
aNeoFile->FSeek(4, SEEK_CUR);
{
unsigned char val[7];
aNeoFile->FRead((char*)&val, 7);
type = val[0];
size = (val[1] << 16) | (val[2] << 8) | (val[3]);
pts = (val[4] << 16) | (val[5] << 8) | (val[6]);
}
if (filesize_from_current(aNeoFile) < size+4)
{
aNeoFile->FSeek(pos, SEEK_SET);
return -1;
}
next = size + aNeoFile->FTell() + 4;
aNeoFile->FSeek(pos, SEEK_SET);
retType = type;
return 0;
}
int flv_read_pts(CNeoFILE* aNeoFile, int& pts)
{
int pos = aNeoFile->FTell();
if (filesize_from_current(aNeoFile) < 4)
{
aNeoFile->FSeek(pos, SEEK_SET);
return -1;
}
aNeoFile->FSeek(4, SEEK_CUR);
{
unsigned char val[7];
aNeoFile->FRead((char*)&val, 7);
pts = (val[4] << 16) | (val[5] << 8) | (val[6]);
}
aNeoFile->FSeek(pos, SEEK_SET);
return 0;
}
int flv_read_if_metabody(CNeoFILE* aNeoFile, int& retType)
{
int type, next, pos, size;
pos = /*ftell(fp)*/aNeoFile->FTell();
int fsize_from_current = filesize_from_current(aNeoFile);
if (fsize_from_current < 15)
return 1;
aNeoFile->FSeek(4, SEEK_CUR);
{
unsigned char val[7];
aNeoFile->FRead((char*)&val, 7);
type = val[0];
size = (val[1] << 16) | (val[2] << 8) | (val[3]);
}
aNeoFile->FSeek(4, SEEK_CUR);
if (size == 0)
return 0;
next = size + aNeoFile->FTell();
if (filesize(aNeoFile) < next)
{
aNeoFile->FSeek(pos, SEEK_SET);
return 1;
}
if (type == FLV_TAG_TYPE_AUDIO) {
aNeoFile->FSeek(pos, SEEK_SET);
retType = FLV_TAG_TYPE_AUDIO;
return 0;
} else if (type == FLV_TAG_TYPE_VIDEO) {
aNeoFile->FSeek(pos, SEEK_SET);
retType = FLV_TAG_TYPE_VIDEO;
return 0;
} else if (type == FLV_TAG_TYPE_META) {
if (size > 13+1+4)
flv_read_metabody(aNeoFile, next);
else /* skip packet */
;
aNeoFile->FSeek(next, SEEK_SET);
return 0;
}
}
int flv_read_metabody(CNeoFILE* aNeoFile, unsigned int next_pos)
{
AMFDataType type;
int keylen;
char buffer[11]; //only needs to hold the string "onMetaData". Anything longer is something we don't want.
keylen = 0;
//first object needs to be "onMetaData" string
type = (AMFDataType)get_byte(aNeoFile);
if (type != AMF_DATA_TYPE_STRING || amf_get_string(aNeoFile, buffer, sizeof(buffer)) < 0 || strcmp(buffer, "onMetaData"))
return -1;
//parse the second object (we want a mixed array)
if(amf_parse_object(aNeoFile, buffer, next_pos, 0) < 0)
return -1;
return 0;
}
int flv_find_next_keyframe(CNeoFILE* aNeoFile, int& duration, int& position)
{
int size, type, flags, next, pos;
pos = aNeoFile->FTell();
while (true)
{
position = aNeoFile->FTell();
aNeoFile->FSeek(4, SEEK_CUR);
{
unsigned char val[7];
aNeoFile->FRead((char*)&val, 7);
type = val[0];
size = (val[1] << 16) | (val[2] << 8) | (val[3]);
duration = (val[4] << 16) | (val[5] << 8) | (val[6]);
}
if (aNeoFile->FEof())
{
aNeoFile->FSeek(pos, SEEK_SET);
return -1;
}
if (filesize_from_current(aNeoFile) < 4)
{
aNeoFile->FSeek(pos, SEEK_SET);
return -1;
}
aNeoFile->FSeek(4, SEEK_CUR);
flags = 0;
if (size == 0)
continue;
next = size + /*ftell(fp)*/aNeoFile->FTell();
if (filesize_from_current(aNeoFile) < size)
{
aNeoFile->FSeek(pos, SEEK_SET);
return -1;
}
if (type == FLV_TAG_TYPE_AUDIO) {
flags = get_byte(aNeoFile);
aNeoFile->FSeek(next, SEEK_SET);
} else if (type == FLV_TAG_TYPE_VIDEO) {
flags = get_byte(aNeoFile);
aNeoFile->FSeek(next, SEEK_SET);
if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)
{
AddKeyFrame(duration, position);
aNeoFile->FSeek(pos, SEEK_SET);
return 0;
}
} else if (type == FLV_TAG_TYPE_META) {
aNeoFile->FSeek(next, SEEK_SET);
}
}
}
int amf_get_string(CNeoFILE* aNeoFile, char *buffer, int buffsize)
{
int length = get_be16(aNeoFile);
if (length >= buffsize) {
aNeoFile->FSeek(length, SEEK_CUR);
return -1;
}
aNeoFile->FRead(buffer, length);
buffer[length] = '\0';
return length;
}
int amf_parse_object(CNeoFILE* aNeoFile, const char *key, unsigned int max_pos, int depth)
{
AMFDataType amf_type;
char str_val[256];
double num_val;
num_val = 0;
amf_type = (AMFDataType)get_byte(aNeoFile);
switch(amf_type) {
case AMF_DATA_TYPE_NUMBER:
num_val = av_int2dbl(get_be64(aNeoFile));
break;
case AMF_DATA_TYPE_BOOL:
num_val = get_byte(aNeoFile);
break;
case AMF_DATA_TYPE_STRING:
if(amf_get_string(aNeoFile, str_val, sizeof(str_val)) < 0)
return -1;
break;
case AMF_DATA_TYPE_OBJECT: {
unsigned int keylen;
while (aNeoFile->FTell() < max_pos - 2 && (keylen = get_be16(aNeoFile))) {
aNeoFile->FSeek(keylen, SEEK_CUR);
if(amf_parse_object(aNeoFile, NULL, max_pos, depth + 1) < 0)
return -1; //if we couldn't skip, bomb out.
}
if(get_byte(aNeoFile) != AMF_END_OF_OBJECT)
return -1;
}
break;
case AMF_DATA_TYPE_NULL:
case AMF_DATA_TYPE_UNDEFINED:
case AMF_DATA_TYPE_UNSUPPORTED:
break; //these take up no additional space
case AMF_DATA_TYPE_MIXEDARRAY:
aNeoFile->FSeek(4, SEEK_CUR);
while(aNeoFile->FTell() < max_pos - 2 && amf_get_string(aNeoFile, str_val, sizeof(str_val)) > 0) {
if(amf_parse_object(aNeoFile, str_val, max_pos, depth + 1) < 0)
return -1;
}
if(get_byte(aNeoFile) != AMF_END_OF_OBJECT)
return -1;
break;
case AMF_DATA_TYPE_ARRAY: {
unsigned int arraylen, i;
arraylen = get_be32(aNeoFile);
for(i = 0; i < arraylen && aNeoFile->FTell() < max_pos - 1; i++) {
if(amf_parse_object(aNeoFile, NULL, max_pos, depth + 1) < 0)
return -1; //if we couldn't skip, bomb out.
}
}
break;
case AMF_DATA_TYPE_DATE:
aNeoFile->FSeek(8+2, SEEK_CUR);
break;
default: //unsupported type, we couldn't skip
return -1;
}
if (depth == 1 && key) { //only look for metadata values when we are not nested and key != NULL
if(amf_type == AMF_DATA_TYPE_BOOL) {
} else if(amf_type == AMF_DATA_TYPE_NUMBER) {
if(!strcmp(key, "duration")) flvGlobalVar.duration = num_val;// * AV_TIME_BASE;
else if(!strcmp(key, "width") && num_val > 0) flvGlobalVar.width = num_val;
else if(!strcmp(key, "height") && num_val > 0) flvGlobalVar.height = num_val;
if(!strcmp(key, "audiocodecid")) flvGlobalVar.audiocodecid = (int)num_val << FLV_AUDIO_CODECID_OFFSET;//flv_set_audio_codec(astream, (int)num_val << FLV_AUDIO_CODECID_OFFSET);
else if(!strcmp(key, "videocodecid")) flvGlobalVar.videocodecid = (int)num_val;//flv_set_video_codec(fp, vstream, (int)num_val);
}
}
return 0;
}
unsigned char get_byte(CNeoFILE* aNeoFile)
{
unsigned char val;
aNeoFile->FRead((char*)&val, 1);
return val;
}
unsigned int get_be16(CNeoFILE* aNeoFile)
{
unsigned int val;
aNeoFile->FRead((char*)&val, 2);
val = ((val & 0xFF) << 8) | ((val & 0xFF00) >> 8);
return val;
}
unsigned int get_be24(CNeoFILE* aNeoFile)
{
unsigned int val;
aNeoFile->FRead((char*)&val, 3);
val = ((val & 0xFF) << 16) | (val & 0xFF00) | ((val & 0xFF0000) >> 16);
return val;
}
unsigned int get_be32(CNeoFILE *aNeoFile)
{
unsigned int val;
aNeoFile->FRead((char*)&val, 4);
val = ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val & 0xFF0000) >> 8) | ((val & 0xFF000000) >> 24);
return val;
}
uint64_t get_be64(CNeoFILE* aNeoFile)
{
uint64_t val;
val = (uint64_t)get_be32(aNeoFile) << 32;
val |= (uint64_t)get_be32(aNeoFile);
return val;
}
double av_int2dbl(int64_t v)
{
//if(v+v > 0xFFEULL<<52)
// return 0.0/0.0;
return ldexp((double)(((v&((1LL<<52)-1)) + (1LL<<52)) * (v>>63|1)), (v>>52&0x7FF)-1075);
}
TFLVGlobalVar& GetFLVGlobalVar()
{
return flvGlobalVar;
}
int FLVCalculateFrameRate(CNeoFILE* aNeoFile)
{
int type, size, next, pts;
int pos = aNeoFile->FTell();
int curpos = pos;
int framecount = 0;
double FrameTimeStamp = 2000;
if (flvGlobalVar.framerate > 0)
return 0;
next = pos;
pts = 0;
if (flvGlobalVar.duration > 0 && flvGlobalVar.duration < 2)
FrameTimeStamp = flvGlobalVar.duration * 1000;
while (pts < FrameTimeStamp)
{
if (aNeoFile->FSize() < next)
{
aNeoFile->FSeek(pos, SEEK_SET);
return 1;
}
aNeoFile->FSeek(next, SEEK_SET);
if (filesize_from_current(aNeoFile) < 11)
{
aNeoFile->FSeek(pos, SEEK_SET);
return 1;
}
curpos = aNeoFile->FTell();
aNeoFile->FSeek(4, SEEK_CUR);
{
unsigned char val[7];
aNeoFile->FRead((char*)&val, 7);
type = val[0];
size = (val[1] << 16) | (val[2] << 8) | (val[3]);
pts = (val[4] << 16) | (val[5] << 8) | (val[6]);
}
if (aNeoFile->FEof())
{
aNeoFile->FSeek(pos, SEEK_SET);
if (FrameTimeStamp < 2000 && abs(FrameTimeStamp - pts) < 0.1)
{
flvGlobalVar.framerate = framecount*1000/pts;
return 0;
}
return 1;
}
if (filesize_from_current(aNeoFile) < 4)
{
aNeoFile->FSeek(pos, SEEK_SET);
return 1;
}
aNeoFile->FSeek(4, SEEK_CUR);
if (size == 0)
continue;
if (type == FLV_TAG_TYPE_VIDEO)
framecount++;
next = size + /*ftell(fp)*/aNeoFile->FTell();
}
aNeoFile->FSeek(pos, SEEK_SET);
if (pts > 10000)
return 1;
flvGlobalVar.framerate = framecount*1000/pts;
if ( flvGlobalVar.framerate >= 24 && flvGlobalVar.framerate <= 26 )
flvGlobalVar.framerate = 25;
if ( flvGlobalVar.framerate >= 29 && flvGlobalVar.framerate <= 31 )
flvGlobalVar.framerate = 29.97;
if ( flvGlobalVar.framerate >= 14 && flvGlobalVar.framerate <= 16 )
flvGlobalVar.framerate = 15;
if (flvGlobalVar.framerate == 0)
return 1;
return 0;
}
void SetDuration(int duration)
{
flvGlobalVar.duration = duration;
}
double FLVGetDuration()
{
return flvGlobalVar.duration;
}
void ResetFrameRateAndCodecID()
{
flvGlobalVar.framerate = 0;
flvGlobalVar.audiocodecid = -1;
flvGlobalVar.videocodecid = -1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -