#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEBUG_INFO
typedef struct FileHeader {
unsigned char type[4]; // UI8 * 3 "FLV"
unsigned char versions; // UI8 版本号
unsigned char stream_info;//UI8 流信息
unsigned int length; // UI32 文件长度
}_FileHeader;
typedef struct TagHeader {
unsigned char type; // UI8 tag类型
unsigned int data_size; // UI24 数据区长度
unsigned int timestemp; // UI24 时间戳
unsigned char time_stamp_extended; //UI8 扩展时间戳
unsigned int stream_id; //UI24 流id
}_TagHeader;
typedef struct Tag {
_TagHeader *header;
unsigned char *data;
}_Tag;
_Tag *malloc_tag()
{
_Tag *tag = malloc(sizeof(_Tag));
if (tag == NULL) {
fprintf(stderr, "malloc tag error\n");
perror("malloc error:");
return NULL;
}
tag->header = NULL;
tag->data = NULL;
return tag;
}
void free_tag(_Tag **tag)
{
if(tag != NULL){
if(*tag == NULL){
return;
}
if ((*tag)->header != NULL) {
free((*tag)->header);
(*tag)->header = NULL;
}
if ((*tag)->data != NULL) {
free((*tag)->data);
(*tag)->data = NULL;
}
*tag = NULL;
}
}
int reverse_32(unsigned int a)
{
union {
int i;
char c[4];
} u, r;
u.i = a;
r.c[0] = u.c[3];
r.c[1] = u.c[2];
r.c[2] = u.c[1];
r.c[3] = u.c[0];
return r.i;
}
int reverse_24(unsigned int a)
{
union {
int i;
char c[4];
} u, r;
u.i = a;
r.c[0] = u.c[2];
r.c[1] = u.c[1];
r.c[2] = u.c[0];
r.c[3] = 0;
return r.i;
}
int read_file_header(FILE *pf, _FileHeader **p_header)
{
int ret = 0;
_FileHeader *header = malloc(sizeof(_FileHeader));
if (header == NULL) {
fprintf(stderr, "malloc file header error\n");
perror("malloc error:");
return -1;
}
memset(header, 0, sizeof(_FileHeader));
//read file type
if ((ret = fread(header->type, 3, 1, pf)) <= 0) {
goto __err_exit;
}
// read version, Usually 1
if ((ret = fread(&(header->versions), 1, 1, pf)) <= 0) {
goto __err_exit;
}
// read stream info
if ((ret = fread(&(header->stream_info), 1, 1, pf)) <= 0) {
goto __err_exit;
}
// read file length
if((ret = fread(&(header->length), 4, 1, pf)) <= 0){
goto __err_exit;
}
header->length = reverse_32(header->length);
*p_header = header;
#ifdef DEBUG_INFO
printf("=========== file header=============\n");
printf("type : %s\n", header->type);
printf("versions : %u\n", header->versions);
printf("stream_info : %u\n", header->stream_info);
printf("length : %d\n", header->length);
printf("====================================\n");
#endif
return 1;
__err_exit:
free(header);
fprintf(stderr, "read file header error\n");
perror("fread error:");
return ret;
}
int check_file_header(_FileHeader *header)
{
// is flv file?
if ((header->type[0] != 0x46/*'F'*/) || (header->type[1] != 0x4C/*'L'*/) || (header->type[2] != 0x56/*'V'*/)) {
fprintf(stderr, "check file header type error\n");
return -1;
}
//UB[7]~UB[3]总为0,
//UB[1]总为0
//UB[2]=1 Audio
if (header->stream_info & 0x04) {
printf("have audio tag\n");
}
//UB[0] = 1 Video
if (header->stream_info & 0x01) {
printf("have video tag\n");
}
return 1;
}
int read_last_tag_size(FILE *pf, unsigned int *p_last_tag_size)
{
int ret = 0;
unsigned int last_tag_size = 0;
if ((ret = fread(&last_tag_size, 4, 1, pf)) <= 0) {
fprintf(stderr, "read last tag size error\n");
perror("fread error:");
return ret;
}
*p_last_tag_size = reverse_32(last_tag_size);
#ifdef DEBUG_INFO
printf("---last tag size %x\n", *p_last_tag_size);
#endif
return 1;
}
int read_tag_header(FILE *pf, _TagHeader **p_header)
{
int ret = 0;
_TagHeader *header = NULL;
header = malloc(sizeof(_TagHeader));
if (header == NULL) {
fprintf(stderr, "malloc tag header error\n");
perror("malloc error:");
return -1;
}
// read tag type , 0x08 is audio, 0x09 is video, 0x12 is script
if ((ret = fread(&(header->type), 1, 1, pf)) <= 0) {
goto __err_exit;
}
// read tag data size
if ((ret = fread(&(header->data_size), 3, 1, pf)) <= 0) {
goto __err_exit;
}
header->data_size = reverse_24(header->data_size);
// read tag timestemp
if ((ret = fread(&(header->timestemp), 3, 1, pf)) <= 0) {
goto __err_exit;
}
header->timestemp = reverse_24(header->timestemp);
//read tag extended time stemp
if ((ret = fread(&(header->time_stamp_extended), 1, 1, pf)) <= 0) {
goto __err_exit;
}
//read stream id
if ((ret = fread(&(header->stream_id), 3, 1, pf)) <= 0) {
goto __err_exit;
}
header->stream_id = reverse_24(header->stream_id);
*p_header = header;
#ifdef DEBUG_INFO
printf("======= tag header =======\n");
printf("tag_type:%x\n", header->type);
printf("data_size:%x\n", header->data_size);
printf("timestemp:%x\n", header->timestemp);
printf("time_stamp_extended:%x\n", header->time_stamp_extended);
printf("stream_id:%x\n", header->stream_id);
#endif
return 1;
__err_exit:
free(header);
fprintf(stderr, "read tag header error\n");
perror("fread error:");
return ret;
}
int read_tag_data(FILE *pf, unsigned char **p_data, unsigned int size)
{
int ret = 0;
unsigned char *data = NULL;
data = malloc(size + 1);
if (data == NULL) {
fprintf(stderr, "malloc tag data error\n");
perror("malloc error:");
return -1;
}
data[size] = 0;
if ((ret = fread(data, size, 1, pf)) <= 0) {
free(data);
fprintf(stderr, "read tag data error\n");
perror("fread error:");
return ret;
}
*p_data = data;
return 1;
}
void audio_tag_data(unsigned char *data)
{
unsigned char audio_info = 0;
unsigned char audio_format = 0;
unsigned char audio_samplerate = 0;
unsigned char audio_samplelenght = 0;
unsigned char audio_type = 0;
// the first byte is the adio info
audio_info = data[0];
//UB[7]~UB[4] is the audo format
// 0 -- 未压缩
// 1 -- ADPCM
// 2 -- MP3
// 5 -- Nellymoser 8kHz momo
// 6 -- Nellymose
audio_format = audio_info & 0xf0;
audio_format >>= 4;
//UB[3]~UB[2] is the sample rate
// 0 -- 5.5kHz
// 1 -- 11kHz
// 2 -- 22kHz
// 3 -- 44kHz
audio_samplerate = audio_info & 0x0c;
audio_samplerate >>= 2;
//UB[1] is the sample length
// 0 -- snd8Bit
// 1 -- snd16Bit
audio_samplelenght = audio_info & 0x02;
audio_samplelenght >>= 1;
//UB[0] is the type
// 0 -- sndMomo
// 1 -- sndStereo
audio_type = audio_info & 0x01;
#ifdef DEBUG_INFO
printf("=======audio_info====\n");
printf("format:%x\n", audio_format);
printf("samplerate:%x\n", audio_samplerate);
printf("samplelenght:%x\n", audio_samplelenght);
printf("type:%x\n", audio_type);
#endif
}
void video_tag_data(unsigned char *data)
{
int size = 0;
unsigned char video_info = 0;
unsigned char video_frame_type = 0;
unsigned char video_code_id = 0;
// the first byte is the adio info
video_info = data[0];
//UB[7]~UB[4] is the video format
// 1 -- keyframe
// 2 -- inner frame
// 3 -- disposable inner frame (H.263 only)
video_frame_type = video_info & 0xf0;
video_frame_type >>= 4;
// UB[3]~UB[0] is the encoder id
// 2 -- Seronson H.263
// 3 -- Screen video
// 4 -- On2 VP6
// 5 -- On2 VP6 without channel
// 6 -- Screen video version 2
video_code_id = video_info & 0x0f;
#ifdef DEBUG_INFO
printf("=======video_info====\n");
printf("frame_type:%x\n", video_frame_type);
printf("code_id:%x\n", video_code_id);
#endif
}
void script_tag_data(unsigned char *data)
{
//Metadata Tag
/*
* 该类型Tag又通常被称为Metadata Tag,
* 会放一些关于FLV视频和音频的参数信息,
* 如duration、width、height等。
* 通常该类型Tag会跟在File Header后面作为第一个Tag出现,
* 而且只有一个。
* 一般来说,该Tag Data结构包含两个AMF包。
* 第一个AMF包封装字符串类型数据,
* 用来装入一个“onMetaData”标志,
* 这个标志与Adobe的一些API调用有,在此不细述。
* 第二个AMF包封装一个数组类型,
* 这个数组中包含了音视频信息项的名称和值。
*/
// amf 封闭在这里不解了,会在以后文章中见到。
}
int read_tag(FILE *pf, unsigned int *p_last_tag_size, _Tag *tag)
{
int ret = 0;
// last tag size
if ((ret = read_last_tag_size(pf, p_last_tag_size)) <= 0) {
return ret;
}
// read tag header
if ((ret = read_tag_header(pf, &(tag->header))) <= 0) {
return ret;
}
//read tag data
if ((ret = read_tag_data(pf, &(tag->data), tag->header->data_size)) <= 0) {
return ret;
}
// 通常第一个音频和视频包都是sequence header
switch(tag->header->type){
case 0x8: //audio
audio_tag_data(tag->data);
break;
case 0x9: //video
video_tag_data(tag->data);
break;
case 0x12: //script
script_tag_data(tag->data);
break;
}
return 1;
}
int main()
{
FILE *pf = NULL;
_FileHeader *file_header = NULL;
unsigned int last_tag_size = 0;
_Tag *tag = NULL;
// open file
if ((pf = fopen("./sample.flv", "rb")) == NULL) {
perror("fopen error:");
return -1;
}
//read file header add check some info
if (read_file_header(pf, &file_header) <= 0) {
fclose(pf);
fprintf(stderr, "read_file_header error\n");
return -1;
}
// check header info
if(check_file_header(file_header) <= 0){
fclose(pf);
free(file_header);
fprintf(stderr, "check_file_header error\n");
return -1;
}
for ( ; ; ) {
free_tag(&tag);
tag = malloc_tag();
if(tag == NULL){
fprintf(stderr, "malloc tag error\n");
perror("malloc error:");
break;
}
if (read_tag(pf, &last_tag_size, tag) <= 0){
break;
}
}
free_tag(&tag);
free(file_header);
fclose(pf);
return 0;
}
flv 解封装
最新推荐文章于 2024-12-13 10:06:05 发布