FLV (Flash Video) 是由 Adobe 公司推出的一种封装格式,主要用于流媒体系统。

FLV 封装的媒体文件具有体积轻巧、封装播放简单等特点,很适合网络应用。

目前各浏览器普遍使用 Flash Player 作为网页播放器,使得安装有浏览器的计算机终端不需要另外安装播放器,

这也是 FLV 格式广为流行的原因之一。

FLV 文件主要由一个 Header 加上由多个 Tag 组成的 Body 构成。

一、FLV Header(UI8表示无符号8位,也就是一个字节;UB[5]表示一个字节中的5位)

 二、FLV Body(Flv Body由一个一个Tag组成,每个Tag前都有一个PerviousTagSize字段,标记着前面一个Tag的大小。)

Tag有三种类型,Audio Tag(音频Tag),Video Tag(视频Tag),script Tag(又称Metadata Tag)

每个Tag由Tag HeaderTag Data组成,对于不同类型的Tag,Tag Header的格式都是相同的(都是11byte的长度),Tag Body的格式就不一样了。

综上所述FLV整体的结构如下图:

 1、AudioTag Data

如果SoundFormat=10,那么音频数据就是AACAUDIODATA。

 2、Video Tag Data

对于H.264数据来说,CodecID = 7。
当CodecID = 7时,视频数据就是AVCVIDEOPACKET格式。

3、Script Tag Data

该类型Tag又通常被称为MetadataTag,会放一些关于FLV视频和音频的元数据信息

如:duration、width、height等。通常该类型Tag会跟在FileHeader后面作为第一个Tag出现,而且只有一个。

第一个AMF包:

第一个字节一般为0x02,表示字符串,第2-3个字节表示字符串的长度,一般为0x000A,后面跟的就是字符串,一般为"onMetaData"。

第二AMF包:

第一个字节为0x08,表示数组,第2-5个字节表示数组元素个数,后面跟着就是数组的元素,格式为:元素名长度(UI16) + 元素名(UI8[n]) + 元素的值(double),最后以“009”结尾

原文链接:https://blog.csdn.net/weixin_42462202/article/details/88661883

此外本程序还可以分离FLV中的视频码流和音频码流。需要注意的是本程序并不能分离一些特定类型的音频(例如AAC)和视频。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> //Important!
#pragma pack(1) #define TAG_TYPE_SCRIPT 18
#define TAG_TYPE_AUDIO 8
#define TAG_TYPE_VIDEO 9 typedef unsigned char byte;
typedef unsigned int uint; typedef struct {
byte Signature[3];
byte Version;
byte Flags;
uint DataOffset;
} FLV_HEADER; typedef struct {
byte TagType;
byte DataSize[3];
byte Timestamp[3];
uint Reserved;
} TAG_HEADER; //reverse_bytes - turn a BigEndian byte array into a LittleEndian integer
uint reverse_bytes(byte *p, char c) {
int r = 0;
int i;
for (i=0; i<c; i++)
r |= ( *(p+i) << (((c-1)*8)-8*i));
return r;
} /**
* Analysis FLV file
* @param url Location of input FLV file.
*/ int simplest_flv_parser(char *url){ //whether output audio/video stream
int output_a=1;
int output_v=1;
//-------------
FILE *ifh=NULL,*vfh=NULL, *afh = NULL; //FILE *myout=fopen("output_log.txt","wb+");
FILE *myout=stdout; FLV_HEADER flv;
TAG_HEADER tagheader;
uint previoustagsize, previoustagsize_z=0;
uint ts=0, ts_new=0; ifh = fopen(url, "rb+");
if ( ifh== NULL) {
printf("Failed to open files!");
return -1;
} //FLV file header
fread((char *)&flv,1,sizeof(FLV_HEADER),ifh); fprintf(myout,"============== FLV Header ==============\n");
fprintf(myout,"Signature: 0x %c %c %c\n",flv.Signature[0],flv.Signature[1],flv.Signature[2]);
fprintf(myout,"Version: 0x %X\n",flv.Version);
fprintf(myout,"Flags : 0x %X\n",flv.Flags);
fprintf(myout,"HeaderSize: 0x %X\n",reverse_bytes((byte *)&flv.DataOffset, sizeof(flv.DataOffset)));
fprintf(myout,"========================================\n"); //move the file pointer to the end of the header
fseek(ifh, reverse_bytes((byte *)&flv.DataOffset, sizeof(flv.DataOffset)), SEEK_SET); //process each tag
do { previoustagsize = _getw(ifh); fread((void *)&tagheader,sizeof(TAG_HEADER),1,ifh); //int temp_datasize1=reverse_bytes((byte *)&tagheader.DataSize, sizeof(tagheader.DataSize));
int tagheader_datasize=tagheader.DataSize[0]*65536+tagheader.DataSize[1]*256+tagheader.DataSize[2];
int tagheader_timestamp=tagheader.Timestamp[0]*65536+tagheader.Timestamp[1]*256+tagheader.Timestamp[2]; char tagtype_str[10];
switch(tagheader.TagType){
case TAG_TYPE_AUDIO:sprintf(tagtype_str,"AUDIO");break;
case TAG_TYPE_VIDEO:sprintf(tagtype_str,"VIDEO");break;
case TAG_TYPE_SCRIPT:sprintf(tagtype_str,"SCRIPT");break;
default:sprintf(tagtype_str,"UNKNOWN");break;
}
fprintf(myout,"[%6s] %6d %6d |",tagtype_str,tagheader_datasize,tagheader_timestamp); //if we are not past the end of file, process the tag
if (feof(ifh)) {
break;
} //process tag by type
switch (tagheader.TagType) { case TAG_TYPE_AUDIO:{
char audiotag_str[100]={0};
strcat(audiotag_str,"| ");
char tagdata_first_byte;
tagdata_first_byte=fgetc(ifh);
int x=tagdata_first_byte&0xF0;
x=x>>4;
switch (x)
{
case 0:strcat(audiotag_str,"Linear PCM, platform endian");break;
case 1:strcat(audiotag_str,"ADPCM");break;
case 2:strcat(audiotag_str,"MP3");break;
case 3:strcat(audiotag_str,"Linear PCM, little endian");break;
case 4:strcat(audiotag_str,"Nellymoser 16-kHz mono");break;
case 5:strcat(audiotag_str,"Nellymoser 8-kHz mono");break;
case 6:strcat(audiotag_str,"Nellymoser");break;
case 7:strcat(audiotag_str,"G.711 A-law logarithmic PCM");break;
case 8:strcat(audiotag_str,"G.711 mu-law logarithmic PCM");break;
case 9:strcat(audiotag_str,"reserved");break;
case 10:strcat(audiotag_str,"AAC");break;
case 11:strcat(audiotag_str,"Speex");break;
case 14:strcat(audiotag_str,"MP3 8-Khz");break;
case 15:strcat(audiotag_str,"Device-specific sound");break;
default:strcat(audiotag_str,"UNKNOWN");break;
}
strcat(audiotag_str,"| ");
x=tagdata_first_byte&0x0C;
x=x>>2;
switch (x)
{
case 0:strcat(audiotag_str,"5.5-kHz");break;
case 1:strcat(audiotag_str,"1-kHz");break;
case 2:strcat(audiotag_str,"22-kHz");break;
case 3:strcat(audiotag_str,"44-kHz");break;
default:strcat(audiotag_str,"UNKNOWN");break;
}
strcat(audiotag_str,"| ");
x=tagdata_first_byte&0x02;
x=x>>1;
switch (x)
{
case 0:strcat(audiotag_str,"8Bit");break;
case 1:strcat(audiotag_str,"16Bit");break;
default:strcat(audiotag_str,"UNKNOWN");break;
}
strcat(audiotag_str,"| ");
x=tagdata_first_byte&0x01;
switch (x)
{
case 0:strcat(audiotag_str,"Mono");break;
case 1:strcat(audiotag_str,"Stereo");break;
default:strcat(audiotag_str,"UNKNOWN");break;
}
fprintf(myout,"%s",audiotag_str); //if the output file hasn't been opened, open it.
if(output_a!=0&&afh == NULL){
afh = fopen("output.mp3", "wb");
} //TagData - First Byte Data
int data_size=reverse_bytes((byte *)&tagheader.DataSize, sizeof(tagheader.DataSize))-1;
if(output_a!=0){
//TagData+1
for (int i=0; i<data_size; i++)
fputc(fgetc(ifh),afh); }else{
for (int i=0; i<data_size; i++)
fgetc(ifh);
}
break;
}
case TAG_TYPE_VIDEO:{
char videotag_str[100]={0};
strcat(videotag_str,"| ");
char tagdata_first_byte;
tagdata_first_byte=fgetc(ifh);
int x=tagdata_first_byte&0xF0;
x=x>>4;
switch (x)
{
case 1:strcat(videotag_str,"key frame ");break;
case 2:strcat(videotag_str,"inter frame");break;
case 3:strcat(videotag_str,"disposable inter frame");break;
case 4:strcat(videotag_str,"generated keyframe");break;
case 5:strcat(videotag_str,"video info/command frame");break;
default:strcat(videotag_str,"UNKNOWN");break;
}
strcat(videotag_str,"| ");
x=tagdata_first_byte&0x0F;
switch (x)
{
case 1:strcat(videotag_str,"JPEG (currently unused)");break;
case 2:strcat(videotag_str,"Sorenson H.263");break;
case 3:strcat(videotag_str,"Screen video");break;
case 4:strcat(videotag_str,"On2 VP6");break;
case 5:strcat(videotag_str,"On2 VP6 with alpha channel");break;
case 6:strcat(videotag_str,"Screen video version 2");break;
case 7:strcat(videotag_str,"AVC");break;
default:strcat(videotag_str,"UNKNOWN");break;
}
fprintf(myout,"%s",videotag_str); fseek(ifh, -1, SEEK_CUR);
//if the output file hasn't been opened, open it.
if (vfh == NULL&&output_v!=0) {
//write the flv header (reuse the original file's hdr) and first previoustagsize
vfh = fopen("output.flv", "wb");
fwrite((char *)&flv,1, sizeof(flv),vfh);
fwrite((char *)&previoustagsize_z,1,sizeof(previoustagsize_z),vfh);
}
#if 0
//Change Timestamp
//Get Timestamp
ts = reverse_bytes((byte *)&tagheader.Timestamp, sizeof(tagheader.Timestamp));
ts=ts*2;
//Writeback Timestamp
ts_new = reverse_bytes((byte *)&ts, sizeof(ts));
memcpy(&tagheader.Timestamp, ((char *)&ts_new) + 1, sizeof(tagheader.Timestamp));
#endif //TagData + Previous Tag Size
int data_size=reverse_bytes((byte *)&tagheader.DataSize, sizeof(tagheader.DataSize))+4;
if(output_v!=0){
//TagHeader
fwrite((char *)&tagheader,1, sizeof(tagheader),vfh);
//TagData
for (int i=0; i<data_size; i++)
fputc(fgetc(ifh),vfh);
}else{
for (int i=0; i<data_size; i++)
fgetc(ifh);
}
//rewind 4 bytes, because we need to read the previoustagsize again for the loop's sake
fseek(ifh, -4, SEEK_CUR); break;
}
default: //skip the data of this tag
fseek(ifh, reverse_bytes((byte *)&tagheader.DataSize, sizeof(tagheader.DataSize)), SEEK_CUR);
} fprintf(myout,"\n"); } while (!feof(ifh)); _fcloseall(); return 0;
}

原文链接:https://blog.csdn.net/leixiaohua1020/article/details/50535082

FLV简介的更多相关文章

  1. hls&flv直播请求过程

    hls&flv直播请求过程 直播类产品层出不穷,从各方面塑造了我们的生活方式.直播产品中,延时是决定用户体验的关键因素,它也将间接决定直播产品的成败.这其间,对延时影响较大的就是直播架构中选择 ...

  2. 【转】GitHub 排名前 100 的安卓、iOS项目简介

    GitHub Android Libraries Top 100 简介 排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不 ...

  3. GitHub Android Libraries Top 100 简介

    本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过 ...

  4. 2016年GitHub 排名前 100 的安卓、iOS项目简介(收藏)

    排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不相关的项目, 所以排名并不具备任何官方效力, 仅供参考学习, 方便初学者 ...

  5. 64.GitHub 排名前100的android项目简介

    GitHub Android Libraries Top 100 简介 排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不 ...

  6. HTML5 视频规范简介

    HTML5 视频规范简介  创建于 2013-02-03, 周日 00:56  作者 白建鹏 HTML 一词是“超文本标记语言”(Hyper-Text Markup Language)的缩写,是用于描 ...

  7. crtmpserver的架构简介

    crtmpserver的架构简介 一.层 Layers . 机器层 Machine layer . 操作系统层 Operating System Layer   This layer is compo ...

  8. [置顶] ffmpg简介以及用它实现音频视频合并(java)

    1.简介     FFmpeg是一个自由软件,可以运行音频和视频多种格式的录影.转档.流功能. 2.下载     源代码 git://git.libav.org/libav.git     Windo ...

  9. GitHub Android Librarys Top 100 简介

    GitHub Android Librarys Top 100 简介 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据GitHub搜索J ...

随机推荐

  1. C语言:位运算加密

    数据加密解密是一个常用的功能,如果你不希望让别人看到文件中的内容,可以通过密钥(也称"密码")将文件的内容加密.比如文本文件(.txt),加密前的内容是能够读懂的,加密后的内容是& ...

  2. Java基础00-方法引用32

    1. 方法引用 Java8新特征之方法引用 1.1 体验方法引用 代码示例: 需求: 1:定义一个接口(Printable):里面定义一个抽象方法: void printString(String s ...

  3. java面向对象程序设计(下)-接口的定义

    抽象类是从多个类中抽象出来的模板,如果将这种抽象进行得更加彻底,则可以提炼出一种更加特殊的"抽象类"-接口(interface),Java9对接口进行了改进,允许在接口中定义默认方 ...

  4. [刘阳Java]_CSS鼠标悬停

    小白在学习前端技术时候,一定会在刚开始的时候学习CSS2和CSS3的相关知识.这篇内容给大家介绍一个CSS鼠标悬停的效果.大家可以先看下面的效果图,然后我们在说一下实现的效果要求吧 上图效果非常简单和 ...

  5. 微信小程序云开发-数据库-用户删除数据

    一.在商品详情页添加[删除单条数据]按钮 进入goodDetail.wxml页面,添加[删除单条数据]按钮,绑定点击事件removeGood()  二.进入goodDetail.js文件,定义remo ...

  6. 电子物流中的EDI 应用

    电子物流中的EDI 应用 背景 EDI 全称是Electronic data interchange, 即电子数据交换.在传统企业里,很多流程上的操作或者通信一般是由纸质媒介完成的,比如说采购订单.发 ...

  7. PAT甲级:1124 Raffle for Weibo Followers (20分)

    PAT甲级:1124 Raffle for Weibo Followers (20分) 题干 John got a full mark on PAT. He was so happy that he ...

  8. Django报错:'Specifying a namespace in include() without providing an app_name '

    环境:win10(64)+pycharm2018.3+python3.7 在网页项目中使用include()方法 项目目录中同时存在app/urls.py和proj/urls.py 在proj/url ...

  9. 【阿菜读论文】ContractFuzzer:fuzzing方法挖掘智能合约漏洞

    论文简介 论文标题:ContractFuzzer: Fuzzing Smart Contracts for Vulnerability Detection 论文链接:ContractFuzzer: F ...

  10. 新一代垃圾回收器ZGC的探索与实践

    ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器,它的设计目标包括: 停顿时间不超过10ms: 停顿时间不会随着堆的大小,或者活跃对象的大小而增加: ...