【音视频数据数据处理 14】【FLV篇】解析FLV视频码流,并分离FLV中的视频及MP3音频文件

标签: 音视频  # 音视频数据处理

封装格式数据在视频播放器中的位置如下所示:
在这里插入图片描述

本文链接:《【音视频数据数据处理 14】【FLV篇】解析FLV视频码流,并分离FLV中的视频及MP3音频文件


一、FLV封装原理

以下原理知识转自:《视音频编解码学习工程:FLV封装格式分析器

FLV(Flash Video)是Adobe公司设计开发的一种流行的流媒体格式,由于其视频文件体积轻巧、封装简单等特点,使其很适合在互联网上进行应用。此外,FLV可以使用Flash Player进行播放,而Flash Player插件已经安装在全世界绝大部分浏览器上,这使得通过网页播放FLV视频十分容易。目前主流的视频网站如优酷网,土豆网,乐视网等网站无一例外地使用了FLV格式。FLV封装格式的文件后缀通常为“.flv”。

总体上看,FLV包括文件头(File Header)和文件体(File Body)两部分,其中文件体由一系列的Tag组成。因此一个FLV文件是如图1结构。
在这里插入图片描述
其中,每个Tag前面还包含了Previous Tag Size字段,表示前面一个Tag的大小。
Tag的类型可以是视频、音频和Script,每个Tag只能包含以上三种类型的数据中的一种。
在这里插入图片描述

在这里插入图片描述


下面详细介绍一下三种Tag的Tag Data部分的结构。

1.1 Tag Data

1.1.1 Audio Tag Data结构(音频Tag)

音频Tag开始的第1个字节包含了音频数据的参数信息,从第2个字节开始为音频流数据。
如图所示。
在这里插入图片描述

  • 第1个字节的前4位的数值表示了音频编码类型
含义
0 Linear PCM,platform endian
1 ADPCM
2 MP3
3 Linear PCM,little endian
4 Nellymoser 16-kHz mono
5 Nellymoser 8-kHz mono
6 Nellymoser
7 G.711 A-law logarithmic PCM
8 G.711 mu-law logarithmic PCM
9 reserved
10 AAC
14 MP3 8-Khz
15 Device-specific sound
  • 第1个字节的第5-6位的数值表示音频采样率
含义
0 5.5kHz
1 11KHz
2 22 kHz
3 44 kHz

PS:从上表可以发现,FLV封装格式并不支持48KHz的采样率。

  • 第1个字节的第7位表示音频采样精度。
含义
0 8 bits
1 16 bits
  • 第1个字节的第8位表示音频类型
含义
0 sndMono
1 sndStereo



1.1.2 Video Tag Data结构(视频Tag)

视频Tag也用开始的第1个字节包含视频数据的参数信息,从第2个字节为视频流数据。
在这里插入图片描述

  • 第1个字节的前4位的数值表示帧类型
含义
1 keyframe (for AVC,a seekable frame)
2 inter frame (for AVC,a nonseekable frame)
3 disposable inter frame (H.263 only)
4 generated keyframe (reserved for server use)
5 video info/command frame
  • 第1个字节的后4位的数值表示视频编码类型
含义
1 JPEG (currently unused)
2 Sorenson H.263
3 Screen video
4 On2 VP6
5 On2 VP6 with alpha channel
6 Screen video version 2
7 AVC



1.1.3 Script Tag Data结构(控制帧)

该类型Tag又通常被称为Metadata Tag,会放一些关于FLV视频和音频的元数据信息如:duration、width、height等。
通常该类型Tag会跟在File Header后面作为第一个Tag出现,而且只有一个。
在这里插入图片描述

  • 第一个AMF包:
    第1个字节表示AMF包类型,一般总是0x02,表示字符串。
    第2-3个字节为UI16类型值,标识字符串的长度,一般总是0x000A(“onMetaData”长度)。
    后面字节为具体的字符串,一般总为“onMetaData”(6F,6E,4D,65,74,61,44,61,74,61)。

  • 第二个AMF包:
    第1个字节表示AMF包类型,一般总是0x08,表示数组。
    第2-5个字节为UI32类型值,表示数组元素的个数。
    后面即为各数组元素的封装,数组元素为元素名称和值组成的对。

常见的数组元素如表7所示。

含义
duration 时长
width 视频宽度
height 视频高度
videodatarate 视频码率
framerate 视频帧率
videocodecid 视频编码方式
audiosamplerate 音频采样率
audiosamplesize 音频采样精度
stereo 是否为立体声
audiocodecid 音频编码方式
filesize 文件大小

二、准备FLV素材

有了以上知识后,我们还需要先准备素材。

还是拿之前的 《HarryPotter.mp4》,我们来把它转为FLV 视频:

ffmpeg -i HarryPotter.mp4 -c:v libx264 -ar 22050 -crf 28 HarryPotter.flv

C:\Users\ciellee\Desktop\YUV420\H.264>ffmpeg -i HarryPotter.mp4 -c:v libx264 -ar 22050 -crf 28 HarryPotter.flv
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'HarryPotter.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: mp41isom
    creation_time   : 2014-12-21T08:40:48.000000Z
  Duration: 00:06:57.51, start: 0.000000, bitrate: 2096 kb/s
    Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1961 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc (default)
    Metadata:
      creation_time   : 2020-08-30T11:01:59.000000Z
      handler_name    : VideoHandler
      encoder         : AVC Coding
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 131 kb/s (default)
    Metadata:
      creation_time   : 2020-08-30T11:01:59.000000Z
      handler_name    : SoundHandler
Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
  Stream #0:1 -> #0:1 (aac (native) -> mp3 (libmp3lame))
Press [q] to stop, [?] for help
[libx264 @ 000001aa36790500] using SAR=1/1
[libx264 @ 000001aa36790500] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 000001aa36790500] profile High, level 3.1, 4:2:0, 8-bit
[libx264 @ 000001aa36790500] 264 - core 158 r2984 3759fcb - H.264/MPEG-4 AVC codec - Copyleft 2003-2019 - 
Output #0, flv, to 'HarryPotter.flv':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: mp41isom
    encoder         : Lavf58.30.100
    Stream #0:0(und): Video: h264 (libx264) ([7][0][0][0] / 0x0007), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], q=-1--1, 30 fps, 1k tbn, 30 tbc (default)
    Metadata:
      creation_time   : 2020-08-30T11:01:59.000000Z
      handler_name    : VideoHandler
      encoder         : Lavc58.55.100 libx264
    Side data:
      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: 18446744073709551615
    Stream #0:1(und): Audio: mp3 (libmp3lame) ([2][0][0][0] / 0x0002), 22050 Hz, stereo, fltp (default)
    Metadata:
      creation_time   : 2020-08-30T11:01:59.000000Z
      handler_name    : SoundHandler
      encoder         : Lavc58.55.100 libmp3lame
frame=12524 fps=153 q=-1.0 Lsize=   29779kB time=00:06:57.51 bitrate= 584.3kbits/s speed=5.11x
video:26022kB audio:3262kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.689755%
[libx264 @ 000001aa36790500] frame I:114   Avg QP:20.23  size: 21436
[libx264 @ 000001aa36790500] frame P:3917  Avg QP:23.84  size:  4162
[libx264 @ 000001aa36790500] frame B:8493  Avg QP:24.80  size:   930
[libx264 @ 000001aa36790500] consecutive B-frames:  5.8%  7.4% 11.7% 75.1%
[libx264 @ 000001aa36790500] mb I  I16..4: 42.8% 48.6%  8.6%
[libx264 @ 000001aa36790500] mb P  I16..4:  4.9%  7.4%  0.2%  P16..4: 22.3%  2.9%  1.1%  0.0%  0.0%    skip:61.3%
[libx264 @ 000001aa36790500] mb B  I16..4:  0.2%  0.3%  0.0%  B16..8: 17.8%  0.5%  0.0%  direct: 0.3%  skip:80.9%  L0:46.5% L1:52.7% BI: 0.8%
[libx264 @ 000001aa36790500] 8x8 transform intra:57.1% inter:89.2%
[libx264 @ 000001aa36790500] coded y,uvDC,uvAC intra: 16.5% 33.5% 3.4% inter: 1.8% 2.4% 0.0%
[libx264 @ 000001aa36790500] i16 v,h,dc,p: 39% 32% 14% 15%
[libx264 @ 000001aa36790500] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 24% 17% 45%  2%  2%  3%  2%  2%  2%
[libx264 @ 000001aa36790500] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 26% 22% 16%  5%  7%  7%  7%  5%  5%
[libx264 @ 000001aa36790500] i8c dc,h,v,p: 62% 19% 16%  2%
[libx264 @ 000001aa36790500] Weighted P-Frames: Y:1.5% UV:0.9%
[libx264 @ 000001aa36790500] ref P L0: 69.8% 12.4% 13.7%  4.0%  0.1%
[libx264 @ 000001aa36790500] ref B L0: 91.5%  7.2%  1.3%
[libx264 @ 000001aa36790500] ref B L1: 97.5%  2.5%
[libx264 @ 000001aa36790500] kb/s:510.63


三、FLV 数据分析

同样,我们使用 hexdump 把生成的 HarryPotter.flv 数据dump出来看下:hexdump.exe HarryPotter.flv > flv.txt
在这里插入图片描述

我们取前几个节字来研究下:

000000  46 4c 56 01 05 00 00 00 09 00 00 00 00 12 00 01 
================>
--->46 4C 56 		:3 byte) 0x46(70 -> F)  0x4C(76 -> L)   0x56(86 -> V)
--->01		 		:1 byte) Version
--->05(0000 0101)	:1 byte) 第6位表示存在音频  第8位表示存在视频
--->00 00 00 09		:4 byte) File Header 到 File Body 开始的字节数,总为 9
--->00 00 00 00		:4 byte) 表示前一个 Tag 的长度
--->12				:1 byte) 音频(0x08)	视频(0x09)	Script data(0x12)

000010  6a 00 00 00 00 00 00 00 02 00 0a 6f 6e 4d 65 74 
================>
--->00 01 6a		:3 byte) Tag Data 部分的大小,362字节
--->00 00 00		:3 byte) Timestamp,该Tag 的时间戳
--->00				:1 byte) Timestamp_ex  时间戳的扩展字节
--->00 00 00		:3 byte) Stream id ,总是0
--->02 00 0a 6f 6e 4d 65 74 	: 从此开始为 Tag Data


本文链接:《【音视频数据数据处理 14】【FLV篇】解析FLV视频码流,并分离FLV中的视频及MP3音频文件

四、代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

typedef struct FLV_Header{
	char Signature[3];	//0x46(70->F)  0x4C(76->L)  0x56(86->V)
	char Version;
	char Flags;
	__int32 Headersize; 
}FLV_Header;		// 由于结构体不参齐,读取时会多读 3 个字节,以对齐 

typedef struct Tag_Body{
	char Type;				//音频(0x08) 视频(0x09) Script data(0x12)
	char Datasize[3];
	char Timestamp[3];
	char Timestamp_ex;
	char SteamID[3];
	//unsigned char *Data;
}Tag_Body;

int Parser_FLV(char *url)
{
	FLV_Header flv_header;
	Tag_Body tag_body;
	char Previous_Tag_Size[4];
	unsigned __int64 Previous_Size=0;
	unsigned int Datasize, Timestamp;
	char tag_type_str[10], audio_tag_str[100], video_tag_str[100], first_byte=0;
	int tmp;
	
	FILE * flv_stream = fopen(url ,"rb+");
	FILE * mp3_output = fopen("flv_output.mp3", "wb+");
	FILE * flv_output = NULL;
	
	// 读取FLV头信息
 	fread((char *)&flv_header, 1, sizeof(FLV_Header), flv_stream); 
 	
 	printf("==================== FLV Header ====================\n");
	printf("Signature : %c %c %c\n",flv_header.Signature[0], flv_header.Signature[1],flv_header.Signature[2]);
	printf("Version   : 0x%x\n", flv_header.Version);
	printf("Flags     : 0x%x %s %s\n", flv_header.Flags, flv_header.Flags&(0x1<<2)==1<<2 ? "存在音频" : " ",
													   flv_header.Flags&(0x1<<0)==1 ? "存在视频" : " "); 
	printf("HeaderSize: 0x%x\n", flv_header.Headersize);
	printf("====================================================\n");
	
	// 由于结构体4byte对齐的原因,前面会读12byte, 此重定位文件指针,向前退3byte 
	fseek(flv_stream, -3, SEEK_CUR);
	
	do{
		// 读取上一个Tag 的大小
		for(tmp = 0; tmp<4; tmp++){
			Previous_Tag_Size[tmp]= fgetc(flv_stream);
		}
		Previous_Size = (((unsigned __int64)1)<<32)*(Previous_Tag_Size[0]&0xff) + (1<<16)*(Previous_Tag_Size[1]&0xff) + (1<<8)*(Previous_Tag_Size[2]&0xff)+ (1<<0)*(Previous_Tag_Size[3]&0xff);
		
		//printf("\n\nPrevious_Tag_Size:  %x %x %x %x = %d\n", Previous_Tag_Size[0]&0xff, Previous_Tag_Size[1]&0xff, Previous_Tag_Size[2]&0xff, Previous_Tag_Size[3]&0xff, Previous_Size);
		
		// 判断是否到文件尾 
		if(feof(flv_stream))
			break; 
		
		// 读取下一个 Tag_Body 
		fread((void *)&tag_body, sizeof(Tag_Body), 1, flv_stream); 
		
		// 计算 Datasize
		Datasize = 65536*(0xff&tag_body.Datasize[0]) + 256*(0xff & tag_body.Datasize[1]) + (0xff&tag_body.Datasize[2]);
		Timestamp = 65536*(0xff&tag_body.Timestamp[0]) + 256*(0xff & tag_body.Timestamp[1]) + (0xff&tag_body.Timestamp[2]);
		//printf("Datasize  %x  %x  %x  = %d \n", 0xff&tag_body.Datasize[0], 0xff&tag_body.Datasize[1], 0xff &tag_body.Datasize[2], Datasize);
		//printf("TimeStamp %x  %x  %x  = %d \n", 0xff&tag_body.Timestamp[0], 0xff&tag_body.Timestamp[1], 0xff&tag_body.Timestamp[2], Timestamp);
		
		
		// Tag Type
		switch(tag_body.Type){
			case 0x08:	sprintf(tag_type_str, "Audio"); break;
			case 0x09:	sprintf(tag_type_str, "Video"); break;
			case 0x12:	sprintf(tag_type_str, "Script"); break;
			default: 	sprintf(tag_type_str, "Unknow"); break;
		}
		
		// 打印信息 
		printf("[%6s] %6d %6d | Type(0x%x) -- ",tag_type_str, Datasize, Timestamp, tag_body.Type);
		
		// 判断是否到文件尾 
		if(feof(flv_stream))
			break; 
			
		switch(tag_body.Type){
			case 0x08:	// Audio
				memset(audio_tag_str, '\0', 100);
				strcat(audio_tag_str,"| ");	
				
				
				// 获取第一个字节数据 
				first_byte = fgetc(flv_stream);
				//fseek(flv_stream, -1, SEEK_CUR);		// 回退1个字节, mp3 保存时不需要这个 类型,可以不回退 
				
				
				// 解析高四位  音频编码类型
				tmp = (first_byte & 0xf0) >> 4;
				switch(tmp){
					case 0:strcat(audio_tag_str,"Linear PCM, platform endian");break;
					case 1:strcat(audio_tag_str,"ADPCM");break;
					case 2:strcat(audio_tag_str,"MP3");break;
					case 3:strcat(audio_tag_str,"Linear PCM, little endian");break;
					case 4:strcat(audio_tag_str,"Nellymoser 16-kHz mono");break;
					case 5:strcat(audio_tag_str,"Nellymoser 8-kHz mono");break;
					case 6:strcat(audio_tag_str,"Nellymoser");break;
					case 7:strcat(audio_tag_str,"G.711 A-law logarithmic PCM");break;
					case 8:strcat(audio_tag_str,"G.711 mu-law logarithmic PCM");break;
					case 9:strcat(audio_tag_str,"reserved");break;
					case 10:strcat(audio_tag_str,"AAC");break;
					case 11:strcat(audio_tag_str,"Speex");break;
					case 14:strcat(audio_tag_str,"MP3 8-Khz");break;
					case 15:strcat(audio_tag_str,"Device-specific sound");break;
					default:strcat(audio_tag_str,"Unknow");break;
				}
				strcat(audio_tag_str,"| ");
				
				//  解析bit 4 bit 5  音频采样率
				tmp = (first_byte & 0x0C)>>2;
				switch(tmp){
					case 0:strcat(audio_tag_str,"5.5-kHz");break;
					case 1:strcat(audio_tag_str,"1-kHz");break;
					case 2:strcat(audio_tag_str,"22-kHz");break;
					case 3:strcat(audio_tag_str,"44-kHz");break;
					default:strcat(audio_tag_str,"Unknow");break;
				}
				strcat(audio_tag_str,"| ");
			
				//  解析bit 6 音频采样精度
				tmp=(first_byte & 0x02)>>1;
				switch(tmp)
				{
					case 0:strcat(audio_tag_str,"8Bit");break;
					case 1:strcat(audio_tag_str,"16Bit");break;
					default:strcat(audio_tag_str,"Unknow");break;
				}
				strcat(audio_tag_str,"| ");
				
				//  解析bit 7 音频类型
				tmp= first_byte & 0x01;
				switch(tmp)
				{
					case 0:strcat(audio_tag_str,"Mono");break;
					case 1:strcat(audio_tag_str,"Stereo");break;
					default:strcat(audio_tag_str,"Unknow");break;
				}
				printf("%s\n", audio_tag_str);
				
				// 开始读取数据
				if(mp3_output != NULL){
					// 保存 data 数据为 mp3 //  Datasize -1 去除前面先读取的一个字节 
					for(tmp=0; tmp< Datasize - 1; tmp++)
						fputc(fgetc(flv_stream), mp3_output);
				}else{
					// 跳过 DATA数据 
					for(tmp=0; tmp< Datasize - 1; tmp++)
						fgetc(flv_stream);
				}
				
				// 当前Tag 解析完毕 
				break;
				
			case 0x09:	// Video
				memset(video_tag_str, '\0', 100);
				strcat(video_tag_str,"| ");	
				
				// 获取第一个字节数据 
				first_byte = fgetc(flv_stream);
				fseek(flv_stream, -1, SEEK_CUR);		// 回退四个字节 
				//printf("first_byte = %x\n", first_byte);
				
				// 解析高四位  音频编码类型
				tmp = (first_byte & 0xf0) >> 4;
				switch(tmp){
					case 1:strcat(video_tag_str,"key frame  ");break;
					case 2:strcat(video_tag_str,"inter frame");break;
					case 3:strcat(video_tag_str,"disposable inter frame");break;
					case 4:strcat(video_tag_str,"generated keyframe");break;
					case 5:strcat(video_tag_str,"video info/command frame");break;
					default:strcat(video_tag_str,"Unknow");break;
				}
				strcat(video_tag_str,"| ");
				
				// 解析低四位  视频编码类型
				tmp = first_byte & 0x0f;
				switch(tmp){
					case 1:strcat(video_tag_str,"JPEG (currently unused)");break;
					case 2:strcat(video_tag_str,"Sorenson H.263");break;
					case 3:strcat(video_tag_str,"Screen video");break;
					case 4:strcat(video_tag_str,"On2 VP6");break;
					case 5:strcat(video_tag_str,"On2 VP6 with alpha channel");break;
					case 6:strcat(video_tag_str,"Screen video version 2");break;
					case 7:strcat(video_tag_str,"AVC");break;
					default:strcat(video_tag_str,"Unknow");break;
				} 
				printf("%s\n", video_tag_str);
				
				// 开始读取数据
				if(flv_output == NULL){
					// 新建文件 
					flv_output = fopen("flv_output.flv", "wb+");
					// 写入FLV HEADER
					tmp = fwrite((char*)&flv_header, 1, sizeof(FLV_Header), flv_output);
					//printf("第一次写入了 %d 个字节 %x  %x  %x  %x  \n",tmp, Previous_Tag_Size[0],Previous_Tag_Size[1],Previous_Tag_Size[2],Previous_Tag_Size[3]);
					
					// 由于结构体4byte对齐的原因,前面会读12byte, 此重定位文件指针,向前退3byte 
					fseek(flv_output, -3, SEEK_CUR);
					for(tmp = 0; tmp<4; tmp++ ){
						Previous_Tag_Size[tmp] = 0x00;
						fwrite(&Previous_Tag_Size[tmp], 1, 1, flv_output);
					}
				}
				
				// Get TimeStamp, 修改时间戳
				Timestamp *= 2;
				tag_body.Timestamp[0] = (Timestamp>>16) & 0xff;
				tag_body.Timestamp[1] = (Timestamp>>8) & 0xff;
				tag_body.Timestamp[2] = (Timestamp>>0) & 0xff;
			 	
			 	Datasize = Datasize + 4;  // 加上Previous_Tag_Size , 除去之前读的 first_byte 
			 	
			 	// 写入
			 	if(flv_output != NULL){
	 				fwrite((char *)&tag_body, 1, sizeof(Tag_Body), flv_output);
	 				//printf("sizeof(Tag_Body) =%d\n", sizeof(Tag_Body));
	 				
					for(tmp = 0; tmp < Datasize; tmp++)
						fputc(fgetc(flv_stream), flv_output);
	 			}else{
			 		for(tmp = 0; tmp < Datasize; tmp++)
						fgetc(flv_stream);
			 	}
			 	
				fseek(flv_stream, -4, SEEK_CUR);		// 回退四个字节 
				//printf("\nvideo 读取结束");
				break;
				
			case 0x12:	// Script
				printf("\n"); 
				fseek(flv_stream, Datasize, SEEK_CUR);	// 跳过控制数据 
				break;
				
			default:
				printf("不支持的 Type (0x%x)类型 ", tag_body.Type);
				return -1;
				break;
		}
			
	}while(!feof(flv_stream));
	
	printf("\n\n程序结束\n\n");
	
	fclose(flv_stream);
	fclose(mp3_output); 
	
	return 0;
}

int main(void){
	Parser_FLV("HarryPotter.flv");
	
	return 0;
}

五、运行结果

==================== FLV Header ====================
Signature : F L V
Version   : 0x1
Flags     : 0x5 存在音频 存在视频
HeaderSize: 0x9
====================================================
[Script]    362      0 | Type(0x12) --
[ Video]     48      0 | Type(0x9) -- | key frame  | AVC
[ Video]    909      0 | Type(0x9) -- | key frame  | AVC
[ Audio]    209     17 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]     44     34 | Type(0x9) -- | inter frame| AVC
[ Audio]    210     43 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]     42     67 | Type(0x9) -- | inter frame| AVC
[ Audio]    210     69 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Audio]    210     95 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]     42    100 | Type(0x9) -- | inter frame| AVC
[ Audio]    210    121 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]     42    134 | Type(0x9) -- | inter frame| AVC
[ Audio]    210    147 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]     50    167 | Type(0x9) -- | inter frame| AVC
[ Audio]    210    174 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]     44    200 | Type(0x9) -- | inter frame| AVC
[ Audio]    210    200 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Audio]    210    226 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
。。。。。。。 省略。。。。。。。 
[ Audio]    210 417271 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Audio]    209 417297 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]    721 417300 | Type(0x9) -- | inter frame| AVC
[ Audio]    210 417323 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]    652 417334 | Type(0x9) -- | inter frame| AVC
[ Audio]    210 417349 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]   1436 417367 | Type(0x9) -- | inter frame| AVC
[ Audio]    210 417375 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]    346 417400 | Type(0x9) -- | inter frame| AVC
[ Audio]    210 417401 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Audio]    210 417427 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]    742 417434 | Type(0x9) -- | inter frame| AVC
[ Audio]    210 417454 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Audio]    210 417480 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Audio]    210 417506 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Audio]    210 417532 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Audio]    210 417558 | Type(0x8) -- | MP3| 22-kHz| 16Bit| Stereo
[ Video]      5 417434 | Type(0x9) -- | key frame  | AVC
[ Video]      5 834868 | Type(0x9) --

程序结束

程序运行结束后,生成flv_output.flvflv_output.mp3 两个分离文件
在这里插入图片描述
使用ffplay 播放分离后的视频文件: flv_output.flv
在这里插入图片描述
使用ffplay 播放分离后音频文件: flv_output.mp3
在这里插入图片描述



视音频数据处理入门:FLV封装格式解析
视音频编解码学习工程:FLV封装格式分析器

版权声明:本文为Ciellee原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Ciellee/article/details/108320340

智能推荐

【音视频数据数据处理 7】【RGB/BMP篇】生成24色 RGB24图 和 BMP图

【音视频数据数据处理 7】【RGB篇】生成一张24色 RGB24图 和 BMP图 一、生成24色 RGB24图代码实现 二、生成24色 BMP图代码实现 先上24色图,及其对应的RGB值。 本文地址:《【音视频数据数据处理 7】【RGB/BMP篇】生成24色 RGB24图 和 BMP图》 好,接下来,我们来生成一张24色彩图,图片格式RGB24。 一、生成24色 RGB24图代码实现 代码如下: ...

【音视频数据数据处理 6】【RGB篇】将RGB24图片转为YUV420格式图片

【音视频数据数据处理 6】【RGB篇】将RGB24图片转为YUV420格式图片 一、RGB24 理论知识 二、YUV420 理论知识 三、RGB24_to_YUV420_I420 代码实现 本文主要内容为,实现将 RGB24图片转为YUV420格式, 写代码之前,我们分别来分析下RGB24和YUV420各自的理论知识。 本文地址:《【音视频数据数据处理 6】【RGB篇】将RGB24图片转为YUV4...

WebRTC(四) Web端音视频数据采集及处理

本文介绍如果通过html代码在浏览器中采集和播放音视频数据。 1 html代码,用于显示视频 2 js代码,用于获取视频流 通过以上js和html代码,就能将采集出摄像头的数据和音频数据。 但是以上方法是有浏览器的兼容性的问题。所以需要在js里面加入以下开源库 完整html代码 预览效果...

Layui parent.layui.open弹框之Iframe 传值处理

Layui open弹框获取值的方法 介绍:Layui 弹框之Iframe传值处理 我的想法 解决 子页面 获取 父页面方法以及元素。 上代码,看图片 原创作品,欢迎来讨论! 介绍:Layui 弹框之Iframe传值处理 本人在使用到layui的iframe版 ,里面使用到了弹框 。 普通弹框:layui.open(); 像这种传递值都没什么问题 , 子页面获取父页面值 或者父页面获取子页面值 全...

外置Tomcat无法使用devtools实现热部署

练手的项目每次有源码或者页面更新都需要重新启动,不能忍,热部署走一波 这个项目是用外置Tomcat启动的 项目层级目录 模块依赖关系:service 依赖于 model 依赖于 api (启动类在service模块中) 引入devTools依赖,确定相关idea配置无误后,发现热部署没有生效 得出结论: devTools无法对使用对外置的tomcat运行的项目生效 于是在网上搜索外置tomcat项...

猜你喜欢

C++跨平台库QT学习7 使用UnitTest单元测试入门

C++跨平台库QT学习7 使用UnitTest单元测试入门 一、新建子目录项目 二、新建控制台项目 三、新建测试用例子目录项目 mycalctest.pro文件内容: 测试用例文件test_mycalctesttest.cpp 在子项目`mycalctest`点右键、运行 一、新建子目录项目 在QT点击菜单 文件-新建文件或项目-其他项目-子目录项目: 二、新建控制台项目 然后继续建一个子项目: ...

Python实用模块(二十五)loguru

软硬件环境 windows 10 64bits anaconda with python 3.7 loguru 0.5.3 前言 Python实用模块(十四)logging https://xugaoxiang.com/2019/12/04/python-module-logging 已 经介绍过了python内置日志模块logging。我们要使用logging,一般来讲,都是需要进行一...

Glide图片加载框架的使用简介与功能介绍

Glide图片加载框架的使用简介 . 1. 在app/build.gradle文件当中添加如下依赖: 2. 在AndroidManifest.xml中声明一下网络权限才行: 3. 开始使用Glide加载图片 with()方法的介绍 作用: 用于创建一个加载图片的实例;with()方法可以接收Context、Activity或者Fragment类型的参数 注意: with()方法中传入的实例会决定G...

编写过滤器解决全局乱码问题

过滤器编写步骤 编写一个类实现javax.servlet.Filter接口 重写接口中所有的方法,其中doFilter方法执行过滤的功能 配置过滤器 在web.xml中配置 使用注解@WebFilter 解决乱码需要添加这句代码:req.setCharacterEncoding(“utf-8”); 字符集与网页的编码要一致 EncodingFilter.java: 过滤器的...

HTML+CSS+JS做一个简易音乐播放器

先给大家看下效果: 实现功能:音乐播放,歌词跟随进度滚动,中间随着音乐播放图片360度旋转 文件目录: 做一个播放器,音乐和歌词事先要下载好,搜一些自己喜欢的封面,让图片360度旋转的样式,通过按钮增删样式达到跟音乐同步进行: 其中歌词匹配才是让我头疼的,所有JS代码部分: 需要所有源码,可以去github上自行下载: https://github.com/lzs1996/MusicPlayer....