flv 解封装

本文档详细介绍了如何解析FLV文件头,并展示了如何读取并解析不同类型的Tag(音频、视频、脚本)。通过使用C语言实现的函数,演示了读取FLV文件、检查文件头、获取Tag信息以及处理不同类型的Tag数据的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值