Pvr改造使用MediaScanner扫描说明文档
主要修改点以及说明如下:
1.MediaFile增加一个PVR(自定义)文件类型
publicstatic final int FILE_TYPE_PVR = 51;
private static final intFIRST_PVR_FILE_TYPE = FILE_TYPE_PVR;
privatestatic final int LAST_PVR_FILE_TYPE = FILE_TYPE_PVR;
FIRST和LAST是在用来判断一个文件是否属于PVR类型时用到
//这个方法是用来判断一个文件是否属于PVR类型
publicstatic boolean isPVRFileType(int fileType) {
return(fileType >= FIRST_PVR_FILE_TYPE &&
fileType <=LAST_PVR_FILE_TYPE);
}
添加
addFileType("PVR",FILE_TYPE_PVR,"PVR/PVR");
这行是将文件扩展名,文件typeid和MIME_TYPE关联,存入sFileTypeMap,sMimeTypeMap中
2MediaScanner.java 里
privatestatic final String[] PVR_PROJECTION = new String[] {
"_id",// 0
"_data",// 1
"date_modified",// 2
};
privatestatic final int ID_PVR_COLUMN_INDEX = 0;
privatestatic final int PATH_PVR_COLUMN_INDEX = 1;
privatestatic final int DATE_MODIFIED_PVR_COLUMN_INDEX = 2;
这块是在建立文件缓存的时候读取的数据库字段。
privateString device; 这个是代表文件存储的设备。
device= null; //清理缓存的时候置为Null
在handleStringTag里增加一个if判断
}else if (name.equalsIgnoreCase("filepath")) {
Stringfilepath = value.trim();
int index =filepath.lastIndexOf("/");
mTitle =filepath.substring(index+1,filepath.len
int fIndex =filepath.indexOf("/",1);
int sIndex =filepath.indexOf("/",fIndex+1);
device =filepath.substring(0,sIndex+1);
}
这个方法是底层读到文件的相关属性时的回调方法。filepath是我在底层返回的属性,可以自己定义需要返回的属性。方法内做了一个简单的字符串处理 取出文件路径的前2个单词。
在toValues方法里增加一个
elseif (MediaFile.isPVRFileType(mFileType)) {
map.put("device",device);
}
这个方法是把要存储的属性放到一个ContentValues里
在endFile中添加 如下代码:
booleanisPVR = MediaFile.isPVRFileType(mFileType);
elseif (isPVR) {
tableUri= Uri.parse("content://media/external/PVR");
这个方法是文件扫描完成后往数据库中添加的的过程。。先判断文件类型是否是PVR, 是的话给出对应的uri.
在prescan中添加如下代码:
c= mMediaProvider.query(Uri.parse("content://media/external/PVR"),PVR_PROJECTION, null, null, null);
if(c != null) {
try{
mOriginalCount =c.getCount();
while(c.moveToNext()) {
longrowId = c.getLong(ID_PVR_COLUMN_INDEX);
String path =c.getString(PATH_PVR_COLUMN_INDEX);
longlastModified = c.getLong(DATE_MODIFIED_PVR_COLUMN_INDEX);
//Only consider entries with absolute path names.
//This allows storing URIs in the database without the
//media scanner removing them.
if(path.startsWith("/")) {
String key = path;
if(mCaseInsensitivePaths) {
key =path.toLowerCase();
}
mFileCache.put(key, newFileCacheEntry(Uri.parse("content://media/external/PVR"),rowId, path,
lastModified));
}
}
}finally {
c.close();
c= null;
}
}
这个方法是做一些扫描前的准备工作,主要是把数据库中相应的记录做一个缓存。扫描完成后与缓存做对比,然后进行数据库的更新操作。这里是从数据库的PVR表中取出现有的记录列表。
3在StagefrightMediaScanner.cpp里添加PVR文件类型
".mkv",".mka", ".webm" ,".pvr"
最终负责处理单个文件的是这个文件。。所以需要增加文件类型到这里。
staticstatus_t HandlePVR(const char *path,MediaScannerClient *client){
LOGE("enterhandlePVR");
if(!client->addStringTag("filepath",path)){
LOGE("add String Tag failed");
}
returnOK;
}
增加一个处理PVR文件的方法,里面简单的回调了上层的方法。
在processFile里增加一个PVR的判断
if(!strcasecmp(extension,".PVR")) {
returnHandlePVR(path,&client);
}
4 MediaProvider相关的改动
在mediaProvider的updateDatabase方法中增加建立PVR表的代码
db.execSQL("CREATETABLE IF NOT EXISTS PVR (" +
"_idINTEGER PRIMARY KEY," +
"titleTEXT," +
"deviceTEXT," +
"mime_typeTEXT," +
"date_modified INTEGER,"+
"_dataTEXT," +
"_sizeINTEGER" +
");");
在quey方法中增加一个case
casePVR:
qb.setTables("PVR");
break;
在inertInternal方法中增加一个case:
casePVR:{
rowId =db.insert("PVR","device",initialValues);
if(rowId > 0) {
newUri =ContentUris.withAppendedId(uri, rowId);
}
break;
}
这个是最终负责将底层传上来的ContentValues插入到数据库中的方法
在getTableAndWhere方法中增加一个case;
casePVR_ID:
out.table= "PVR";
where = "_id="+ uri.getPathSegments().get(2);
break;
这里主要是底层调用Provider删除方法时,通过这个方法给出表名和删除条件
在mediaProvider中定义2个Match值
privatestaticfinalintPVR= 700;
privatestaticfinalintPVR_ID= 701;
然后添加UriMatch
URI_MATCHER.addURI("media","*/PVR",PVR);
URI_MATCHER.addURI("media","*/PVR/#",PVR_ID);
以上就是在pvr改造中所涉及的修改之处。
注意:测试时直接在路径下touch**.PVR,是搜不到的,可能是size为0的文件搜不到,可以将其他格式文件改为.PVR的文件可以搜到
2.扫描文件内容
以上是如何根据文件扩展名扫描文件,pvr在改造时还添加了扫描文件内容的功能,在录制完成时在Pvr的系统服务中会将当前录制完成的节目信息(包括节目名称,录制时间,时长,音量,等)写入一个pvrInfor.pvr文件中.同时发送扫描广播,让系统MediaScanner扫描系统文件,将pvrInfor.pvr中存储的信息扫描出来存入数据库pvr中,同时扫描出当前录制的节目流文件,信息存在数据ts中。具体实现如下:
(1)创建pvr文件
framework/base/dvb/java/cn/ccdt/pvr/PvrRecordService的stopRecord中调用stPvrFileInfo的createFile()
/**
* 将文件写入pvrInfor.xpp
*/
publicvoidcreateFile(){
Log.v("stPvrFIleInfo","###@@@####start create xpp file!!! path="+path);
//检查路径
Filefile = newFile(path);
if(!file.exists()){
file.mkdir();
}
//创建文件
FilefileDoc = newFile(path,"pvrInfor.pvr");
try{
fileDoc.createNewFile();
//写文件
File fileWrite = newFile(fileDoc.getAbsolutePath());
if(fileWrite.exists()){
FileOutputStream fos = newFileOutputStream(fileWrite);
OutputStreamWriter os = newOutputStreamWriter(fos, "utf-8");
BufferedWriter bw = newBufferedWriter(os);
//bw.write(serviceIdent + "\n");
bw.write(newString(name.getBytes("utf-8"),"GBK")+ "\n");
bw.write(path+ "\n");
bw.write(storageDevicePath+ "\n");
bw.write(beginTime+ "\n");
bw.write(totalTime+ "\n");
bw.write(lastPlayTime+ "\n");
bw.write(volume+ "\n");
bw.write(recordType+ "\n");
bw.flush();//写入
Log.v("stPvrFIleInfo","###@@@#### create xpp file ok !!! ");
}
}catch(IOException e) {
e.printStackTrace();
}
}
(2)扫描文件:
1>framework/base/media/java/android/media/MediaScanner.java中 prescan()从数据库中查询文件状态缓存
2>扫描文件内容:
framework/base/media/libstagefright/StagefrightMediaScanner.cpp中
添加函数:
staticstatus_t HandlePvr(constchar *path,MediaScannerClient *client){
LOGE("@@@@enter HandlePvr %s",path);
/*读取文件*/
FILE *pFile = fopen(path,"rb");
long lSize;
char * buffer;
size_t result;
if (pFile == NULL){
LOGE("HandlePvr @@@@@@ !pFile fopen faild! path=%s",path);
return UNKNOWN_ERROR;
}
/*获取文件大小*/
fseek (pFile , 0 ,SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
staticconst char *allColumnName[] = {
"name", "path", "device", "begin_time","total_time",
"lastplay_ime", "volumn", "record_type"
};
charbuffer_line[512];
inti = 0;
for(;i< 8 ; i++){
fgets(buffer_line,512,pFile);
/*上发扫描值*/
if(!client->addStringTag(allColumnName[i],buffer_line)){
LOGE("add String Tag failed");
}
}
fclose(pFile);
returnOK;
}
3> framework/base/media/java/android/media/MediaScanner.java中 endFile();提供uri将数据插入数据库
提供插入和查询Mediascanner扫描到的数据:
framework/base/package/providers/MediaProvider/src/com/android/provider/media/MediaProvider.java
在query方法中添加我们自定义文件格式的case:
casePVR:
Log.w(TAG,">>>>>>>>>>>>>>>>>>>MediaProvierYOU QUERY PVR qb "+qb);
qb.setTables("pvr");
break;
casePVR_ID:
Log.w(TAG,">>>>>>>>>>>>>>>>>>>MediaProvierYOU QUERY PVR_ID index= "+uri.getLastPathSegment());
qb.setTables("pvr");
qb.appendWhere("_id="+ uri.getLastPathSegment());
break;
caseTS:
Log.w(TAG,">>>>>>>>>>>>>>>>>>>MediaProvierYOU QUERY ts qb "+qb);
qb.setTables("ts");
break;
caseTS_ID:
Log.w(TAG,">>>>>>>>>>>>>>>>>>>MediaProvierYOU QUERY TS_ID index= "+uri.getLastPathSegment());
qb.setTables("ts");
qb.appendWhere("_id="+ uri.getLastPathSegment());
break;
在insertInternal中添加插入代码
casePVR:
Log.d(TAG,">>>>>>>>insertpvr data");
rowId = db.insert("pvr","device",initialValues);
if(rowId > 0) {
newUri =ContentUris.withAppendedId(uri, rowId);
}
break;
caseTS:
Log.d(TAG,">>>>>>>>insertts data");
rowId = db.insert("ts","device",initialValues);
if(rowId > 0) {
newUri =ContentUris.withAppendedId(uri, rowId);
}
break;
以上操作中只对.pvr文件进行了文件内容扫描,ts文件只是记录一些通用信息。
注意:修改了.cpp文件要重新编译so文件替换nfs中的so。这里我们修改了StagefrightMediaScanner.cpp在编译framework.jar的时候会一同编译出一个libstagefright.so文件。