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 Header和Tag 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. python twain模块

    >>> help(twain) Help on module twain: NAME twain - Created on Sep 4, 2011 DESCRIPTION @auth ...

  2. 全面了解Nginx到底能做什么

    最近做项目需要动静分离,便用nginx的反向代理来实现.后来看到一篇好文,记录下. 来自https://www.jianshu.com/p/8bf73d1a758c 前言 本文只针对Nginx在不加载 ...

  3. 【LeetCode】389.找不同

    389.找不同 知识点:哈希表.抵消思想: 题目描述 给定两个字符串 s 和 t,它们只包含小写字母. 字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母. 请找出在 t 中被添加的字母. ...

  4. tcpdump软件使用

    tcpdump是一个抓包工具, -w 选项是把抓到的包写到二进制文件中,一般扩展名是.cap或.dmp,但tcpdump程序创建文件时并不添加扩展名,可自己指定. -i 是指定要抓包的interfac ...

  5. python基础之os模块操作

    # os模块 目录相关内置库import os# . 当前目录 .. 返回上一级目录# 1. os.path.abspath() --获取当前文件的绝对路径(不包含os模块.py) pwd# path ...

  6. DNS Bind日志详述

    简介 在默认情况下,BIND9 把日志消息写到 /var/log/messages 文件中,而这些日志消息是非常少的,主要就是启动,关闭的日志记录和一些严重错误的消息:而将调试日志信息写入 BIND ...

  7. spring boot(二)整合mybatis plus+ 分页插件 + 代码生成

    先创建spring boot项目,不知道怎么创建项目的 可以看我上一篇文章 用到的环境 JDK8 .maven.lombok.mysql 5.7 swagger 是为了方便接口测试 一.Spring ...

  8. 每天五分钟Go - 循环语句

    带条件的for循环 for init; condition; post { } 示例代码 for i:=0;i<10;i++{ fmt.Println("current:", ...

  9. 🔥 LeetCode 热题 HOT 100(81-90)

    337. 打家劫舍 III 思路:后序遍历 + 动态规划 推荐题解:树形 dp 入门问题(理解「无后效性」和「后序遍历」) /** * Definition for a binary tree nod ...

  10. [CTF] CTF入门指南

    CTF入门指南 何为CTF ? CTF(Capture The Flag)夺旗比赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式.CTF起源于1996年DEFCON全球黑客大会 ...