FLV文件主要由两部分组成:Header和Body。

1. Header

header部分记录了flv的类型、版本等信息,是flv的开头,一般都差不多,占9bytes。具体格式如下:

文件类型 3 bytes

“FLV”

版本 1 byte

一般为0x0000 0001 (1)

流信息 1 byte

倒数第一位是1表示有视频,倒数第三位是1表示有音频.

其余备用字段必须为0, 音视频都具备为0x0000 0101 (5)

header长度 4 bytes

整个header的长度,一般为9; 大于9表示下面还有扩展信息 (9)

2. Body

body部分由一个个Tag组成,每个Tag的下面有一块4bytes的空间,用来记录这个tag的长度,这个后置用于逆向读取处理。

2.1.Tag

每个Tag由也是由两部分组成的:Tag Header和Tag Data。Tag Header里存放的是当前Tag的类型、数据区(Tag Data)长度等信息,具体如下:

名称 长度

介绍

Tag类型 1 bytes 8:音频
9:视频
18:脚本 (这里是一些描述信息)
其他:保留
数据区长度 3 bytes 数据区(Tag Data)的长度
时间戳 3 bytes

整数,单位是毫秒。对于脚本型的tag总是0.

相对于FLV文件的第一个TAG时戳。第一个tag的时戳总是0。

——不是时戳增量,rtmp中是时戳增量。

时间戳扩展 1 bytes 将时间戳扩展为4bytes,代表高8位。很少用到
StreamsID 3 bytes 总是0

2.2.Tag Data

数据区根据Tag类型的不同可分为三种,音频数据、视频数据和脚本数据。

2.2.1.音频数据

第一个byte是音频的信息,格式如下:

名称 长度 介绍
音频格式 4 bits 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
11 = Speex
14 = MP3 8-Khz
15 = Device-specific sound
采样率 2 bits 0 = 5.5-kHz
1 = 11-kHz
2 = 22-kHz
3 = 44-kHz
对于AAC总是3
采样的长度 1 bit 0 = snd8Bit
1 = snd16Bit
压缩过的音频都是16bit
音频类型 1 bit 0 = sndMono
1 = sndStereo
对于AAC总是1

第2byte开始就是音频流数据了。

2.2.2.视频数据

和音频数据一样,第一个byte是视频信息,格式如下:

名称 长度 介绍
帧类型 4 bits 1: keyframe (for AVC, a seekable frame)
2: inter frame (for AVC, a non-seekable frame)
3: disposable inter frame (H.263 only)
4: generated keyframe (reserved for server use only)
5: video info/command frame
编码ID 4 bits 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

2.2.3脚本数据

脚本Tag一般只有一个,是flv的第一个Tag,用于存放flv的信息,比如duration、audiodatarate、creator、width等等信息。

首先介绍下脚本的数据类型。所有数据都是以数据类型+(数据长度)+数据的格式出现的,数据类型占1byte,数据长度看数据类型是否存在,后面才是数据。

可参考AMF数据格式。
其中数据类型的种类有:

  • 0 = Number type
  • 1 = Boolean type
  • 2 = String type
  • 3 = Object type
  • 4 = MovieClip type
  • 5 = Null type
  • 6 = Undefined type
  • 7 = Reference type
  • 8 = ECMA array type
  • 10 = Strict array type
  • 11 = Date type
  • 12 = Long string type

如果类型为String,后面的2bytes为字符串的长度(Long String是4bytes),再后面才是字符串数据;如果是Number类型,后面的8bytes为Double类型的数据;Boolean类型,后面1byte为Bool类型。

知道了这些后再来看看flv中的脚本,一般开头是0x02,表示String类型,后面的2bytes为字符串长度,一般是0x000a(“onMetaData”的长度),再后面就是字符串“onMetaData”。好像flv格式的文件都有onMetaData标记,在运行ActionScript的时候会用到它。后面跟的是0x08,表示ECMA Array类型,这个和Map比较相似,一个键跟着一个值。键都是String类型的,所以开头的0x02被省略了,直接跟着的是字符串的长度,然后是字符串,再是值的类型,也就是上面介绍的那些了。

文件内存显示:

3. 源码解析

flv的格式还是比较简单的,header部分很简洁,body部分都是由一个个tag,tag的话也就三种,脚本tag一般只有一个的,我想这也是flv能成为在线视频格式的原因吧。

只要了解了格式,我们就可以写个程序来解析flv文件了,下面我们看源码解析:

FLVFile.h

#pragma once
#include "VideoFile.h" /* PACKET_TYPE_... 0x00 */
#define PACKET_TYPE_CHUNK_SIZE 0x01
/* PACKET_TYPE_... 0x02 */
#define PACKET_TYPE_BYTES_READ_REPORT 0x03
#define PACKET_TYPE_CONTROL 0x04
#define PACKET_TYPE_SERVER_BW 0x05
#define PACKET_TYPE_CLIENT_BW 0x06
/* PACKET_TYPE_... 0x07 */
#define PACKET_TYPE_AUDIO 0x08
#define PACKET_TYPE_VIDEO 0x09
/* PACKET_TYPE_... 0x0A */
/* PACKET_TYPE_... 0x0B */
/* PACKET_TYPE_... 0x0C */
/* PACKET_TYPE_... 0x0D */
/* PACKET_TYPE_... 0x0E */
#define PACKET_TYPE_FLEX_STREAM_SEND 0x0F
#define PACKET_TYPE_FLEX_SHARED_OBJECT 0x10
#define PACKET_TYPE_FLEX_MESSAGE 0x11
#define PACKET_TYPE_SCRIPT 0x12
#define PACKET_TYPE_SHARED_OBJECT 0x13
#define PACKET_TYPE_INVOKE 0x14
/* PACKET_TYPE_... 0x15 */
#define PACKET_TYPE_FLASH_VIDEO 0x16 /************************************************************************************************************
header部分记录了flv的类型、版本等信息,是flv的开头,一般都差不多,占9bytes. 具体格式如下;
文件类型    3 bytes "FLV";
版本 1 byte 一般为0x01;
流信息 1 byte 倒数第一位是1表示有视频,倒数第三位是1表示有音频,倒数第二、四位必须为0;
header长度 4 bytes 整个header的长度,一般为9;大于9表示下面还有扩展信息;
************************************************************************************************************/
struct FlvHeader
{
byte Type[];
byte Version;
byte StreamInfo;
byte HeaderSize[];
}; /************************************************************************************************************
每个Tag由也是由两部分组成的:Tag Header和Tag Data;
Tag Header里存放的是当前Tag的类型、数据区(Tag Data)长度等信息;
具体如下; 名称 长度 介绍;
Tag类型 1 bytes 8:音频 \ 9:视频 \ 18:脚本 \ 其他:保留;
数据区长度 3 bytes 在数据区的长度;
时间戳 3 bytes 整数,单位是毫秒 对于脚本型的tag总是0;
时间戳扩展 1 bytes 将时间戳扩展为4bytes,代表高8位 很少用到;
StreamsID 3 bytes 总是0;
************************************************************************************************************/
struct TagHeader
{
byte TagType;
byte DataSize[];
byte Timestamp[];
byte TimeExtend;
byte StreamsID[];
}; class CFLVFile: public CVideoFile
{
public:
CFLVFile();
virtual ~CFLVFile(); virtual bool ParseFile(); protected:
bool ParseFLVHeader();
bool ParseFLVBody(); bool ParseAudioData(int iDataSize);
bool ParseVideoData(int iDataSize);
bool ParseScriptData(int iDataSize);
};

FLVFile.cpp

#include "FLVFile.h"
#include "amf.h" CFLVFile::CFLVFile()
{
} CFLVFile::~CFLVFile()
{
} bool CFLVFile::ParseFile()
{
if (!ParseFLVHeader())
{
return false;
} if (!ParseFLVBody())
{
return false;
} return true;
} bool CFLVFile::ParseFLVHeader()
{
printf("******************************FLV Header******************************\n");
// FLV Header;
{
FlvHeader flvHeader;
int iSize = sizeof(FlvHeader);
if (iSize != fread(&flvHeader, , iSize, m_pFile))
{
printf("Read FLV Header is Error. \n");
return false;
} printf("\t File Type \t : %c %c %c \n", flvHeader.Type[], flvHeader.Type[], flvHeader.Type[]);
printf("\t Version \t : %d \n", flvHeader.Version);
printf("\t Stream Info \t : %d \n", flvHeader.StreamInfo);
printf("\t Header Length \t : %d \n\n", ByteToInt(flvHeader.HeaderSize, sizeof(flvHeader.HeaderSize)));
} return true;
} bool CFLVFile::ParseFLVBody()
{
printf("******************************FLV Body******************************\n"); bool bStop = false;
while (!bStop)
{
printf("\n ************************Tag Header************************ \n"); _getw(m_pFile); TagHeader tagHeader;
int iTagHeaderSize = sizeof(TagHeader);
if (iTagHeaderSize != fread(&tagHeader, , iTagHeaderSize, m_pFile))
{
// 读完了;
printf("\t Read File Finished. \n");
bStop = true;
} printf("\t Tag Type \t : %d \n", tagHeader.TagType);
printf("\t DataSize \t : %d \n", ByteToInt(tagHeader.DataSize, sizeof(tagHeader.DataSize)));
printf("\t Timestamp \t : %d \n", ByteToInt(tagHeader.Timestamp, sizeof(tagHeader.Timestamp)));
printf("\t TimeExtend \t : %d \n", tagHeader.TimeExtend);
printf("\t StreamsID \t : %d \n", ByteToInt(tagHeader.StreamsID, sizeof(tagHeader.StreamsID))); printf(" ************************Tag Body************************ \n");
const int iDataSize = ByteToInt(tagHeader.DataSize, sizeof(tagHeader.DataSize)); switch (tagHeader.TagType)
{
case PACKET_TYPE_AUDIO:
{
// 音频数据;
ParseAudioData(iDataSize);
}
break;
case PACKET_TYPE_VIDEO:
{
// 视频数据;
ParseVideoData(iDataSize);
}
break;
case PACKET_TYPE_SCRIPT:
{
// 类型数据;
ParseScriptData(iDataSize);
}
break;
default:
{
printf("\t Read Tag Body %d bytes. \n\n", iDataSize);
fseek(m_pFile, iDataSize, SEEK_CUR);
}
break;
}
} return true;
} /************************************************************************************************************
第一个字节是音频信息格式,格式如下; 名称 长度 介绍; 音频格式 4 bits
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
11 = Speex
14 = MP3 8-Khz
15 = Device-specific sound 采样率 2 bits (对于AAC总是3)
0 = 5.5-kHz
1 = 11-kHz
2 = 22-kHz
3 = 44-kHz 采样的长度 1 bit (压缩过的音频总是16bit)
0 = snd8Bit
1 = snd16Bit 音频类型 1 bit (对于AAC总是1)
0 = sndMono
1 = sndStereo 第二个字节开始就是音频流数据了;
************************************************************************************************************/
bool CFLVFile::ParseAudioData(int iDataSize)
{
// 解析音频格式;
byte audioHeader;
fread(&audioHeader, , sizeof(audioHeader), m_pFile); char strAudioInfo[] = { };
{
byte audioFormat = audioHeader >> ;
switch (audioFormat)
{
case :strcat(strAudioInfo, "Linear PCM, platform endian"); break;
case :strcat(strAudioInfo, "ADPCM"); break;
case :strcat(strAudioInfo, "MP3"); break;
case :strcat(strAudioInfo, "Linear PCM, little endian"); break;
case :strcat(strAudioInfo, "Nellymoser 16-kHz mono"); break;
case :strcat(strAudioInfo, "Nellymoser 8-kHz mono"); break;
case :strcat(strAudioInfo, "Nellymoser"); break;
case :strcat(strAudioInfo, "G.711 A-law logarithmic PCM"); break;
case :strcat(strAudioInfo, "G.711 mu-law logarithmic PCM"); break;
case :strcat(strAudioInfo, "reserved"); break;
case :strcat(strAudioInfo, "AAC"); break;
case :strcat(strAudioInfo, "Speex"); break;
case :strcat(strAudioInfo, "MP3 8-Khz"); break;
case :strcat(strAudioInfo, "Device-specific sound"); break;
default:strcat(strAudioInfo, "UNKNOWN"); break;
}
strcat(strAudioInfo, "| ");
} {
byte sampBits = audioHeader << ;
sampBits = sampBits >> ;
switch (sampBits)
{
case :strcat(strAudioInfo, "5.5-kHz"); break;
case :strcat(strAudioInfo, "1-kHz"); break;
case :strcat(strAudioInfo, "22-kHz"); break;
case :strcat(strAudioInfo, "44-kHz"); break;
default:strcat(strAudioInfo, "UNKNOWN"); break;
}
strcat(strAudioInfo, "| ");
} {
byte sampLen = audioHeader << ;
sampLen = sampLen >> ;
switch (sampLen)
{
case :strcat(strAudioInfo, "8Bit"); break;
case :strcat(strAudioInfo, "16Bit"); break;
default:strcat(strAudioInfo, "UNKNOWN"); break;
}
strcat(strAudioInfo, "| ");
} {
byte audioType = audioHeader << ;
audioType = audioType >> ;
switch (audioType)
{
case :strcat(strAudioInfo, "Mono"); break;
case :strcat(strAudioInfo, "Stereo"); break;
default:strcat(strAudioInfo, "UNKNOWN"); break;
}
strcat(strAudioInfo, "| ");
} printf("\t %s audio data: %d bytes \n", strAudioInfo, iDataSize - );
fseek(m_pFile, iDataSize - , SEEK_CUR); return true;
} /************************************************************************************************************
第一个字节是视频信息格式,格式如下; 名称 长度 介绍; 帧类型 4 bits
1: keyframe(for AVC, a seekable frame)
2 : inter frame(for AVC, a non - seekable frame)
3 : disposable inter frame(H.263 only)
4 : generated keyframe(reserved for server use only)
5 : video info / command frame 编码ID 4 bits
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 第二个字节开始就是视频流数据了;
************************************************************************************************************/
bool CFLVFile::ParseVideoData(int iDataSize)
{
// 解析音频格式;
byte videoHeader;
fread(&videoHeader, , sizeof(videoHeader), m_pFile); char strVideoInfo[] = { };
{
byte frameType = videoHeader >> ;
switch (frameType)
{
case :strcat(strVideoInfo, "key frame "); break;
case :strcat(strVideoInfo, "inter frame"); break;
case :strcat(strVideoInfo, "disposable inter frame"); break;
case :strcat(strVideoInfo, "generated keyframe"); break;
case :strcat(strVideoInfo, "video info/command frame"); break;
default:strcat(strVideoInfo, "UNKNOWN"); break;
}
strcat(strVideoInfo, "| ");
} {
byte sampBits = videoHeader << ;
sampBits = sampBits >> ;
switch (sampBits)
{
case :strcat(strVideoInfo, "JPEG (currently unused)"); break;
case :strcat(strVideoInfo, "Sorenson H.263"); break;
case :strcat(strVideoInfo, "Screen video"); break;
case :strcat(strVideoInfo, "On2 VP6"); break;
case :strcat(strVideoInfo, "On2 VP6 with alpha channel"); break;
case :strcat(strVideoInfo, "Screen video version 2"); break;
case :strcat(strVideoInfo, "AVC"); break;
default:strcat(strVideoInfo, "UNKNOWN"); break;
}
strcat(strVideoInfo, "| ");
} printf("\t %s audio data: %d bytes \n", strVideoInfo, iDataSize - );
fseek(m_pFile, iDataSize - , SEEK_CUR); return true;
} /************************************************************************************************************
脚本Tag一般只有一个,是flv的第一个Tag;
用于存放flv的信息,比如duration、audiodatarate、creator、width等; 首先介绍下脚本的数据类型;
所有数据都是以数据类型+(数据长度)+数据的格式出现的,数据类型占1byte,数据长度看数据类型是否存在,后面才是数据; AMF数据格式解析;
************************************************************************************************************/
bool CFLVFile::ParseScriptData(int iDataSize)
{
// 解析类型信息;
byte* pData = new byte[iDataSize];
byte* pDataEnd = pData + iDataSize;
fread(pData, , iDataSize, m_pFile); while (pDataEnd - pData > )
{
switch (pData[])
{
case AMF_NUMBER: // 数字(double);
{
//double dVal = AMF_DecodeNumber(pFileFlv);
}
break;
case AMF_BOOLEAN: // 布尔;
{ }
break;
case AMF_STRING: // 字符串;
{
pData = pData + ; AVal valName;
AMF_DecodeString((char*)pData, &valName);
pData = pData + (valName.av_len + );
}
break;
case AMF_OBJECT: // 对象;
{ }
break;
case AMF_MOVIECLIP: // 保留,未使用;
break;
// AMF_NULL, // null;
// AMF_UNDEFINED, // 未定义;
// AMF_REFERENCE, // 引用;
case AMF_ECMA_ARRAY: // 数组;
{
pData = pData + ; // 数组元素个数;
int iArrLen = ByteToInt(pData, );
pData = pData + ; AMFObject obj;
int iSize = pDataEnd - pData;
int nRes = AMF_DecodeArray(&obj, (char*)pData, iSize, iArrLen, TRUE);
if (nRes == -)
{
return false;
} for (int i = ; i < iArrLen; i++)
{
std::string strName(obj.o_props[i].p_name.av_val, obj.o_props[i].p_name.av_len);
switch (obj.o_props[i].p_type)
{
case AMF_NUMBER: // 数字(double);
{
printf("\t %s : %.0f\n", strName.c_str(), obj.o_props[i].p_vu.p_number);
}
break;
case AMF_STRING: // 字符串;
{
std::string strValue(obj.o_props[i].p_vu.p_aval.av_val, obj.o_props[i].p_vu.p_aval.av_len);
printf("\t %s : %s\n", strName.c_str(), strValue.c_str());
}
break;
default:
break;
}
} pData = pData + iSize;
}
break;
// AMF_OBJECT_END, // 对象结束(0x09);
// AMF_STRICT_ARRAY, // 严格的数组;
// AMF_DATE, // 日期;
// AMF_LONG_STRING, // 长字符串;
// AMF_UNSUPPORTED, // 未支持;
// AMF_RECORDSET, // 保留,未使用;
// AMF_XML_DOC, // xml文档;
// AMF_TYPED_OBJECT, // 有类型的对象;
// AMF_AVMPLUS, // 需要扩展到AMF3;
// AMF_INVALID = 0xff // 无效的;
default:
break;
}
} return true;
}

main.cpp

#include <windows.h>
#include "FLVFile.h"
#include <assert.h> // FLV 文件解析;
bool FLVParse_Test()
{
CFLVFile flvFile;
return flvFile.LoadFile("../testfile/flv_test.flv");
} void main()
{
// FLV;
bool bRes = FLVParse_Test();
assert(bRes);
}

运行结果:

项目源码下载地址:https://github.com/kingsunc/AVFileParse

FLV文件格式分析(附源码)的更多相关文章

  1. leaflet 结合 d3.js 实现 geojson 数据地形剖面分析(附源码下载)

    前言 leaflet 入门开发系列环境知识点了解: leaflet api文档介绍,详细介绍 leaflet 每个类的函数以及属性等等 leaflet 在线例子 leaflet 插件,leaflet ...

  2. Cesium专栏-空间分析之剖面分析(附源码下载)

    Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...

  3. Cesium-空间分析之通视分析(附源码下载)

    Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...

  4. Code First Entity Framework 6化被动为主动之explicit loading模式实战分析( 附源码)

    在使用Entity Framework加载关联实体时,可以有三种方式: 1.懒加载(lazy Loading); 2.贪婪加载(eager loading); 3.显示加载(explicit load ...

  5. [C#]委托实例分析(附源码)

    一直都听说C#中的委托与事件非常重要,都没有什么切身的体会,而这次通过做一个WinForm二次开发的项目才真正感觉到了委托与事件的犀利之处. 1.C#中的事件和委托的作用? 事件代表一个组件能够被关注 ...

  6. Cesium专栏-填挖方分析(附源码下载)

    Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精度,渲染质量以 ...

  7. Vue路由实现之通过URL中的hash(#号)来实现不同页面之间的切换(图表展示、案例分析、附源码详解)

    前言 本篇随笔主要写了Vue框架中路由的基本概念.路由对象属性.vue-router插件的基本使用效果展示.案例分析.原理图解.附源码地址获取. 作为自己对Vue路由进行页面跳转效果知识的总结与笔记. ...

  8. C#编程总结(七)数据加密——附源码

    C#编程总结(七)数据加密——附源码 概述 数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为“密文”,使其只能在输入相应的密钥之后才能显示出本来内容 ...

  9. Entity Framework在Asp.net MVC中的实现One Context Per Request(附源码)

    上篇中"Entity Framework中的Identity map和Unit of Work模式", 由于EF中的Identity map和Unit of Work模式,EF体现 ...

  10. Remote验证及其改进(附源码)

    Remote验证及其改进(附源码) 表单中的输入项,有些是固定的,不变的验证规则,比如字符长度,必填等.但有些是动态的,比如注册用户名是否存在这样的检查,这个需要访问服务器后台才能解决.这篇文章将会介 ...

随机推荐

  1. Codeforces Round #622 (Div. 2).C2 - Skyscrapers (hard version)

    第二次写题解,请多多指教! http://codeforces.com/contest/1313/problem/C2 题目链接 不同于简单版本的暴力法,这个数据范围扩充到了五十万.所以考虑用单调栈的 ...

  2. BZOJ3473&&BZOJ3277串

    BZOJ3473&&BZOJ3277串 题面 自己找去 HINT 对于所有串建立一个广义后缀自动机,对于每一个节点开一个set表示这个节点接受的子串在哪些串里出现过,然后在parent ...

  3. IDEA常用的几个插件

    目录 1. 阿里巴巴代码检测插件 2. Json转Pojo插件 3. mybatis辅助插件 4. 翻译插件 5. markdown插件 6. RestfulToolKit插件 IDEA常用插件 1. ...

  4. C# 读取Excel到DataTable两种方式对比

    方式一 OLEDB读取 数据库引擎 优点:读取速度快,依据sheet排序读取 缺点:对于Excel版本依赖强,无法读取指定sheet 错误提示:本地计算机未指定 Microsoft.ACE.OLEDB ...

  5. 使用nohup不产生log文件方法

    思想 无法阻止nohup产生日志可以将其定向到空文件实现 实现 $ nohup xxx >/dev/null 2>&1 &

  6. [转]TCP/IP 协议基础(一)

    参考书籍为<图解tcp/ip>-第五版.这篇随笔,主要内容还是TCP/IP所必备的基础知识,包括计算机与网络发展的历史及标准化过程(简述).OSI参考模型.网络概念的本质.网络构建的设备等 ...

  7. 841. 字符串哈希(hash)

    给定一个长度为n的字符串,再给定m个询问,每个询问包含四个整数l1,r1,l2,r2l1,r1,l2,r2,请你判断[l1,r1l1,r1]和[l2,r2l2,r2]这两个区间所包含的字符串子串是否完 ...

  8. Linux下用Bash语言实现输出最大值的功能

    题目链接: 题目描述 编写一个程序,输入a.b.c三个值,输出其中最大值. 输入 一行数组,分别为a b c 输出 a b c其中最大的数 样例输入 10 20 30 样例输出 30 复习下Linux ...

  9. Codeforces 832A. Sasha and Sticks

    It's one more school day now. Sasha doesn't like classes and is always bored at them. So, each day h ...

  10. luogu P2158 [SDOI2008]仪仗队 (欧拉函数)

    欧拉函数裸题 可惜我太久没做题忘了欧拉函数是什么了... 注意判断一下n = 1的情况就好了 #include <cstdio> using namespace std; ; typede ...