鸿蒙NEXT开发实战往期必看文章:
一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开发!
“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通)
HarmonyOS NEXT应用开发案例实践总结合(持续更新......)
HarmonyOS NEXT应用开发性能优化实践总结(持续更新......)
场景一:视频播放预加载,边下边播
方案
1. 创建一个沙箱文件,并获取沙箱文件的readFd和writeFd。
2. 通过.new rcp.Request(DOWNLOAD_URL)创建网络下载请求request,配置request的TracingConfiguration,在onDataReceive回调中通过fs.writeSync传入沙箱文件的writeFd,将下载的数据流写入本地沙箱文件,将fs.writeSync返回写入字节大小作为网络下载大小downloadSize,根据downloadSize和下载大小(默认1024*1024字节,AVPlayer默认缓存为1M)配置request的transferRange属性,控制网络下载的起始字节和结束字节。
3. 通过RCP的session.fetch传入request下载获取网络视频资源。
4. 配置AVPlayer的datasrc属性,在datasrc的回调函数中,通过fs.readSync传入沙箱文件的readFd,将沙箱文件的数据写入内存buffer,沙箱文件大小为0时开启网络下载,当pos(表示填写的数据在资源文件中的位置)小于沙箱文件100kb时,再次开启网络下载进而实现分段下载,该回调函数在AVPlayer解析数据时触发,在边下边播的场景中,会不断触发该回调。
5. 设置AVPlayer播放资源,将datasrc设置给AVPlayer。
核心代码
控制网络下载的起始字节和结束字节。
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#fdfdfd"><span style="color:#000000"><code class="language-plain">function download(length: number) {
console.info('MineRcp download from = ' + downloadSize + 'to = ' + (downloadSize + length - 1));
request.transferRange = {
from: downloadSize,
to: downloadSize + length - 1,
};
downloadStarted = true;
session.fetch(request).then(() => {
downloadStarted = false;
}).catch(() => {
downloadStarted = false;
});
}</code></span></span></span></span>
onDataReceive回调中通过fs.writeSync传入沙箱文件的writeFd,将下载的数据流写入本地沙箱文件。
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#fdfdfd"><span style="color:#000000"><code class="language-plain">request.configuration = {
tracing: {
httpEventsHandler: {
onDataReceive: (incomingData: ArrayBuffer) => {
const writeLength = fs.writeSync(this.writeFd, GetDecodedBuffer(incomingData));
downloadSize += writeLength;
console.info('MineRcp recieve Length = ' + writeLength.toString() + " , recieve = " + downloadSize);
return incomingData.byteLength;
}
}
}
};</code></span></span></span></span>
配置AVPlayer的datasrc属性。
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#fdfdfd"><span style="color:#000000"><code class="language-plain">let src: media.AVDataSrcDescriptor = {
fileSize: -1,
callback: (buf: ArrayBuffer, length: number, pos?: number) => {
// buffer,ArrayBuffer类型,表示被填写的内存,必选。
//
// - length,number类型,表示被填写内存的最大长度,必选。
//
// - pos,number类型,表示填写的数据在资源文件中的位置,可选,当fileSize设置为-1时,该参数禁止被使用。
// - 返回值,number类型,返回要填充数据的长度。
let num = 0;
if (buf === undefined || length === undefined) {
return -1;
}
num = fs.readSync(this.readFd, buf);
console.info('MineRcp cacheBuffer after checkBuffer Length = ' + num.toString() + ', pos: ' + pos +
', downloadSize: ' +
downloadSize);
if (num > 0) {
if (pos != undefined && downloadSize - pos < 100 * 1024) {
console.info('MineRcp data not enough, download more');
let downloadLength = 1024 * 1024;
if (this.fileSize - downloadSize <= downloadLength) {
downloadLength = this.fileSize - downloadSize;
}
if (!downloadStarted) {
download(downloadLength);
}
}
return num;
}
if (num === 0) {
console.info('MineRcp no data read, download more');
if (!downloadStarted) {
let downloadLength = 1024 * 1024;
if (this.fileSize - downloadSize <= downloadLength) {
downloadLength = this.fileSize - downloadSize;
}
download(downloadLength);
}
return 0;
}
return -1;
}
}
src.fileSize = this.fileSize;
this.isSeek = false; // 支持seek操作
this.player.dataSrc = src;</code></span></span></span></span>