Millons of lines of code
10 Nov 2013ref: http://www.informationisbeautiful.net/visualizations/million-lines-of-code/
ref: http://www.informationisbeautiful.net/visualizations/million-lines-of-code/
String xml = "<?xml version=\"1.0\"?>\n"
+ "<user>\n"
+ "<name>lnmcc</name>\n"
+ "<age>10</age>\n";
class XMLUser {
String name;
String age;
public XMLUser() {
name = "";
age = "";
}
}
private class UserXMLHandler extends DefaultHandler {
static final int NONE = 0;
static final int NAME = 1;
static final int AGE = 2;
int state = NONE;
static final String NAME_ELEMENT = "name";
static final String AGE_ELEMENT = "age";
@Override
public void startDocument() throws SAXException {
aUser = new XMLUser();
}
@Override
public void endDocument() throws SAXException {
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if(localName.equalsIgnoreCase(NAME_ELEMENT)) {
state = NAME;
} else if(localName.equalsIgnoreCase(AGE_ELEMENT)) {
state = AGE;
} else {
state = NONE;
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String stringChars = new String(ch, start, length);
if(state == NAME) {
aUser.name += stringChars.trim();
} else if(state == AGE) {
aUser.age += stringChars.trim();
}
}
SAXParserFactory aSaxParserFactory = SAXParserFactory.newInstance();
try {
SAXParser aSaxParser = aSaxParserFactory.newSAXParser();
XMLReader anXMLReader = aSaxParser.getXMLReader();
UserXMLHandler aUserXMLHandler = new UserXMLHandler();
anXMLReader.setContentHandler(aUserXMLHandler);
anXMLReader.parse(new InputSource(new ByteArrayInputStream(xml.getBytes())));
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
double hexStr2double(const unsigned char* hex, const unsigned length) {
double ret = 0;
char hexstr[length * 2];
memset(hexstr, 0, sizeof(hexstr));
for(unsigned int i = 0; i < length; i++) {
sprintf(hexstr + i * 2, "%02x", hex[i]);
}
sscanf(hexstr, "%llx", (unsigned long long*)&ret);
return ret;
}
int main() {
double ret = 0;
//40 9e 00 00 00 00 00 00
unsigned char buffer[8] = {0x40, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
ret = hexStr2double(buffer, 8);
printf("%f\n", ret);
//40 89 00 00 00 00 00 00
ret = 0;
unsigned char buffer2[8] = {0x40, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
ret = hexStr2double(buffer2, 8);
printf("%f\n", ret);
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/lv"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
list_view.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<ImageView
android:id="@+id/imgView"
android:layout_width="80dp"
android:layout_height="80dp" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
MainActivity.java
public class MainActivity extends Activity implements OnItemClickListener {
Cursor cursor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.lv);
String[] thumbCols = { MediaStore.Video.Thumbnails.DATA,
MediaStore.Video.Thumbnails.VIDEO_ID };
String[] videoCols = { MediaStore.Video.Media._ID,
MediaStore.Video.Media.DATA, MediaStore.Video.Media.TITLE,
MediaStore.Video.Media.MIME_TYPE };
cursor = managedQuery(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
videoCols, null, null, null);
ArrayList<VideoInfo> videos = new ArrayList<VideoInfo>();
if (cursor != null && cursor.moveToFirst()) {
do {
VideoInfo vf = new VideoInfo();
int id = cursor.getInt(cursor
.getColumnIndex(MediaStore.Video.Media._ID));
//查询缩略图
Cursor thumbCursor = managedQuery(
MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI,
thumbCols, MediaStore.Video.Thumbnails.VIDEO_ID + "="
+ id, null, null);
if (thumbCursor != null && thumbCursor.moveToFirst()) {
vf.thumbPath = thumbCursor.getString(thumbCursor
.getColumnIndex(MediaStore.Video.Thumbnails.DATA));
Log.v("Debug thumPath:", vf.thumbPath);
}
vf.videoPath = cursor.getString(cursor
.getColumnIndex(MediaStore.Video.Media.DATA));
vf.title = cursor.getString(cursor
.getColumnIndex(MediaStore.Video.Media.TITLE));
vf.mimeType = cursor.getString(cursor
.getColumnIndex(MediaStore.Video.Media.MIME_TYPE));
videos.add(vf);
} while (cursor.moveToNext());
listView.setAdapter(new VideoGalleryAdapter(this, videos));
listView.setOnItemClickListener(this);
}
}
private class VideoInfo {
String videoPath;
String thumbPath;
String mimeType;
String title;
}
private class VideoGalleryAdapter extends BaseAdapter {
private Context context;
private ArrayList<VideoInfo> videos;
public VideoGalleryAdapter(Context _context,
ArrayList<VideoInfo> _videos) {
context = _context;
videos = _videos;
}
@Override
public int getCount() {
return videos.size();
}
@Override
public Object getItem(int position) {
return videos.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//复用同一个View
if(convert == null) {
LayoutInflater inflater;
// 绑定一个layout xml文件
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.list_view, null);
ImageView thumb = (ImageView) row.findViewById(R.id.imgView);
if (videos.get(position).thumbPath != null) {
thumb.setImageURI(Uri.parse(videos.get(position).thumbPath));
}
TextView title = (TextView) row.findViewById(R.id.tv);
title.setText(videos.get(position).title);
return row;
} else {
ImageView thumb = (ImageView) convertView.findViewById(R.id.imgView);
if (videos.get(position).thumbPath != null) {
thumb.setImageURI(Uri.parse(videos.get(position).thumbPath));
}
TextView title = (TextView) convertView.findViewById(R.id.tv);
title.setText(videos.get(position).title);
return convertView;
}
}
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
if (cursor.moveToPosition(arg2)) {
int filePathIdx = cursor
.getColumnIndex(MediaStore.Video.Media.DATA);
int mimeTypeIdx = cursor
.getColumnIndex(MediaStore.Video.Media.MIME_TYPE);
String filePath = cursor.getString(filePathIdx);
String mimeType = cursor.getString(mimeTypeIdx);
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
File file = new File(filePath);
intent.setDataAndType(Uri.fromFile(file), mimeType);
startActivity(intent);
}
}
}
ref: Pro Android Media: Developing Graphics, Music, Video and Rich Media Apps for Smartphones and Tablets
git clone https://github.com/lnmcc/VideoGallery.git
FLV文件由Header和Body两部分组成。
域名 | 类型 | 说明 |
---|---|---|
Signature | UI8 | Signature byte always ‘F’ (0x46) |
Signature | UI8 | Signature byte always ‘L’ (0x4C) |
Signature | UI8 | Signature byte always ‘V’ (0x56) |
Version | UI8 | File version (for example, 0x01 for FLV version 1) |
TypeFlagsReserved | UB[5] | Must be 0 |
TypeFlagsAudio | UB[1] | Audio tags are present |
TypeFlagsReserved | UB[1] | Must be 0 |
TypeFlagsVideo | UB[1] | Video tags are present |
DataOffset | UI32 | Offset in bytes from start of file to start of body (that is, size of header) |
FLV文件都是以’F”L”V’3个字符开始。在FLV version 1 中,DataOffset一般是9,也就是说FLV的头占据9个字节。
FLV文件头解析片段:
#define FLV_HEAD_LEN 9
FlvHeader* FlvReader::readHeader() {
unsigned char *header = new unsigned char[FLV_HEAD_LEN];
size_t readBytes = fread(header, sizeof(unsigned char), FLV_HEAD_LEN, m_fp);
if(readBytes == FLV_HEAD_LEN) {
if(header[0] != 'F' || header[1] != 'L' || header[2] != 'V') {
cerr << "Not a FLV file!!!" << endl;
delete header;
return NULL;
}
return new FlvHeader(header, FLV_HEAD_LEN);
} else {
delete header;
return NULL;
}
}
域名 | 类型 | 说明 |
---|---|---|
PreviousTagSize0 | UI32 | Always 0 |
Tag1 | FLVTAG | First tag |
PreviousTagSize1 | UI32 | Size of previous tag, including itsheader. For FLV version 1, this valueis 11 plus the DataSize of the previoustag. |
Tag2 | FLVTAG | Second tag |
… | … | … |
图片来自网络
FLV的Body部分是由许多个TAG组成,每一个TAG也是有Tag Header和Tag Body组成。查看上图注意到,每一个TAG之前都有一个PreviousTagSize,一个unsigned int值,用来指示本TAG之前那个TAG的长度,唯一的例外是紧跟在FLV Header之后的那个PreviousTagSize,它的值一定是0 。
每一个TAG也是由Tag Header和Tag Body组成。
域名 | 类型 | 说明 |
---|---|---|
TagType | UI8 | Type of this tag. Values are:8: audio9: video18: script dataall others: reserved |
DataSize | UI24 | Length of the data in the Data field |
Timestamp | UI24 | Time in milliseconds at which thedata in this tag applies. This value isrelative to the first tag in the FLVfile, which always has a timestampof 0 |
TimestampExtended | UI8 | Extension of the Timestamp field toform a UI32 value. This fieldrepresents the upper 8 bits, whilethe previous Timestamp fieldrepresents the lower 24 bits of thetime in milliseconds |
StreamID | UI24 | Always 0 |
Data | If TagType = 8AUDIODATAIf TagType = 9VIDEODATAIf TagType = 18SCRIPTDATAOBJECT | Body of the tag |
一个TagHeader占据11个字节
script data: 十六进制值为0x12(18)。这个类型的Tag通常被称为Metadata Tag,存有关于FLV视频和音频的参数信息,如duration、width、height等。通常该类型Tag会紧跟在FLV Header之后,一个FLV只能有一个Metadata Tag。 metadata Tag由两个AMF(Action Message Format)组成:
第1个AMF: 第1个字节表示AMF包类型,一般为0x02,表示后面跟的数据是字符串
第2,3个字节为UI16类型值,表示后面字符串的长度,一般为0x000A(即“onMetaData”的长度)
第4-13个字节为字符串数据,一般为“onMetaData”
第2个AMF: 第1个字节表示AMF包类型,一般为0x08,表示后面的数据部分是一个数组
第2-5个字节为UI32类型值,表示数组元素的个数,下图所示00000011(HEX) = 17(DEC),表示名称/值数组长度为17
后面即为各数组元素的封装,数组元素为名称/值对。表示方法如下:
第1-2个字节表示元素名称的长度,假设为L,读取后面的L个字节即可得到这个名称字符串。下图所示0008(HEX)=8(DEC),表示这个名称字符串的长度为8 (实际就是“duration”的长度)
“duration”字符串
接下来1byte是值类型,下图的00(HEX)表示Number type,占8byte,这样后面的8byte即为值(这里需要把16进制数转换成double)
AMF的格式:数据类型 + 数据长度 + 数据
数据类型占 1byte
AMF数据类型有:
0 = Number type (DOUBLE : 8byte)
1 = Boolean type (UI8 : 1byte)
2 = String type (2byte)
3 = Object type
4 = MovieClip type
5 = Null type
6 = Undefined type
7 = Reference type (UI16 : 2byte)
8 = ECMA array type
10 = Strict array type
11 = Date type
12 = Long string type (4byte)
上图中的0x12(18)意味着这个Tag是一个script。后面3个字节是Tag Body的大小。
读取一个MetaData的代码片段:
FlvMetaData* FlvReader::readMeta() {
unsigned char tagHeader[TAG_HEAD_LEN];
size_t readBytes;
fseek(m_fp, 9 + 4, SEEK_SET);
//read Tag header
readBytes = fread(tagHeader, sizeof(unsigned char), TAG_HEAD_LEN, m_fp);
if(readBytes == TAG_HEAD_LEN) {
//0x12: meta data
if(tagHeader[0] == 0x12 ) {
unsigned int tagBodySize = 0;
tagBodySize |= tagHeader[1];
tagBodySize = tagBodySize << 8;
tagBodySize |= tagHeader[2];
tagBodySize = tagBodySize << 8;
tagBodySize |= tagHeader[3];
//cerr << "MetaBody size: " << tagBodySize << endl;
unsigned char *tag = new unsigned char[tagBodySize + TAG_HEAD_LEN];
memcpy(tag, tagHeader, TAG_HEAD_LEN);
readBytes = fread(&tag[TAG_HEAD_LEN], sizeof(unsigned char), tagBodySize, m_fp);
if(readBytes == tagBodySize) {
return new FlvMetaData(tag, tagBodySize + TAG_HEAD_LEN);
} else {
delete tag;
return NULL;
}
}
}
return NULL;
}
分析一个MetaData的代码片段:
void FlvMetaData::parseMeta() {
unsigned int arrayLen = 0;
unsigned int offset = TAG_HEAD_LEN + 13;
unsigned int nameLen = 0;
double numValue = 0;
bool boolValue = false;
if(m_meta[offset++] == 0x08) {
arrayLen |= m_meta[offset++];
arrayLen = arrayLen << 8;
arrayLen |= m_meta[offset++];
arrayLen = arrayLen << 8;
arrayLen |= m_meta[offset++];
arrayLen = arrayLen << 8;
arrayLen |= m_meta[offset++];
} else {
//TODO:
cerr << "metadata format error!!!" << endl;
return ;
}
for(unsigned int i = 0; i < arrayLen; i++) {
numValue = 0;
boolValue = false;
nameLen = 0;
nameLen |= m_meta[offset++];
nameLen = nameLen << 8;
nameLen |= m_meta[offset++];
char name[nameLen + 1];
memset(name, 0, sizeof(name));
memcpy(name, &m_meta[offset], nameLen);
name[nameLen + 1] = '\0';
offset += nameLen;
switch(m_meta[offset++]) {
case 0x0: //Number type
numValue = hexStr2double(&m_meta[offset], 8);
offset += 8;
break;
case 0x1: //Boolean type
if(offset++ != 0x00) {
boolValue = true;
}
break;
case 0x2: //String type
nameLen = 0;
nameLen |= m_meta[offset++];
nameLen = nameLen << 8;
nameLen |= m_meta[offset++];
offset += nameLen;
break;
case 0x12: //Long string type
nameLen = 0;
nameLen |= m_meta[offset++];
nameLen = nameLen << 8;
nameLen |= m_meta[offset++];
nameLen = nameLen << 8;
nameLen |= m_meta[offset++];
nameLen = nameLen << 8;
nameLen |= m_meta[offset++];
offset += nameLen;
break;
//FIXME:
default:
break;
}
if(strncmp(name, "duration", 8) == 0) {
m_duration = numValue;
} else if(strncmp(name, "width", 5) == 0) {
m_width = numValue;
} else if(strncmp(name, "height", 6) == 0) {
m_height = numValue;
} else if(strncmp(name, "framerate", 9) == 0) {
m_framerate = numValue;
} else if(strncmp(name, "videodatarate", 13) == 0) {
m_videodatarate = numValue;
} else if(strncmp(name, "audiodatarate", 13) == 0) {
m_audiodatarate = numValue;
} else if(strncmp(name, "videocodecid", 12) == 0) {
m_videocodecid = numValue;
} else if(strncmp(name, "audiosamplerate", 15) == 0) {
m_audiosamplerate = numValue;
} else if(strncmp(name, "audiosamplesize", 15) == 0) {
m_audiosamplesize = numValue;
} else if(strncmp(name, "audiocodecid", 12) == 0) {
m_audiocodecid = numValue;
} else if(strncmp(name, "stereo", 6) == 0) {
m_stereo = boolValue;
}
}
}
git clone https://github.com/lnmcc/FlvParser.git