Created
April 5, 2020 06:56
-
-
Save hsiaosiyuan0/a7b215b53b48b9e66d2e0bad9eb8d1dd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function toAscii(i: number) { | |
return [i >> 24, (i & 0xff0000) >> 16, (i & 0xff00) >> 8, i & 0xff] | |
.map((c) => String.fromCharCode(c)) | |
.join(""); | |
} | |
type Optional<T> = { r: T; e: null } | { r: null; e: Error }; | |
export async function deflate<T>(p: Promise<T>): Promise<Optional<T>> { | |
return p | |
.then((res) => Promise.resolve<{ r: T; e: null }>({ r: res, e: null })) | |
.catch((err) => | |
Promise.resolve<{ r: null; e: Error }>({ r: null, e: err }) | |
); | |
} | |
const rRange = /(\d+)-(\d+)\/(\d+)/; | |
interface FetchRangeParams { | |
from: number; | |
to: number; | |
url: string; | |
} | |
interface FetchRangeResult { | |
begin: number; | |
end: number; | |
total: number; | |
bytes: ArrayBuffer; | |
} | |
async function fetchRange({ | |
from, | |
to, | |
url, | |
}: FetchRangeParams): Promise<Optional<FetchRangeResult>> { | |
const resp = await deflate( | |
fetch(url, { | |
headers: { | |
Range: `bytes=${from}-${to}`, | |
}, | |
}) | |
); | |
if (resp.e) return { r: null, e: resp.e }; | |
const mathResults = resp.r.headers.get("Content-Range")?.match(rRange); | |
if (!mathResults) return { r: null, e: new Error("deformed response") }; | |
const [, begin, end, total] = mathResults; | |
const bytes = await resp.r.arrayBuffer(); | |
return { | |
r: { | |
begin: parseInt(begin, 10), | |
end: parseInt(end, 10), | |
total: parseInt(total, 10), | |
bytes, | |
}, | |
e: null, | |
}; | |
} | |
interface FindMoovResult { | |
size: number; | |
from: number; | |
times: number; | |
} | |
async function locateMoov(url: string): Promise<Optional<FindMoovResult>> { | |
const times = 10; | |
let i = times; | |
let from = 0; | |
let to = 8; | |
while (times) { | |
const resp = await fetchRange({ | |
from, | |
to, | |
url, | |
}); | |
if (resp.e) return { r: null, e: resp.e }; | |
const dv = new DataView(resp.r.bytes, 0, resp.r.bytes.byteLength); | |
const size = dv.getUint32(0); | |
const boxtype = toAscii(dv.getUint32(4)); | |
if (boxtype === "moov") { | |
return { r: { size, from, times: times - i }, e: null }; | |
} | |
console.log(`box is [${boxtype}], continue seeking...`); | |
from = from + size; | |
to = from + 8; | |
i--; | |
} | |
return { r: null, e: new Error("cannot find moov") }; | |
} | |
(async () => { | |
const url = "https://www.dropbox.com/s/fl02xpyhz8693b0/mv.mp?dl=1"; | |
const resp = await locateMoov(url); | |
if (resp.e) { | |
console.error(resp.e); | |
return; | |
} | |
console.log( | |
`found moov at ${resp.r.from}, size is ${resp.r.size} after ${resp.r.times} times seeking` | |
); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment