From 05a1ee205d04ac726cf885492871e8a098bf2e7f Mon Sep 17 00:00:00 2001 From: Joshua Seigler Date: Wed, 24 Nov 2021 16:19:00 -0500 Subject: [PATCH] improve readme --- README.md | 167 +- dist/favicon.5c4f16e7.ico | Bin 34494 -> 0 bytes dist/index.379dd93c.js | 145687 ---------------------------------- dist/index.379dd93c.js.map | 1 - 4 files changed, 14 insertions(+), 145841 deletions(-) delete mode 100644 dist/favicon.5c4f16e7.ico delete mode 100644 dist/index.379dd93c.js delete mode 100644 dist/index.379dd93c.js.map diff --git a/README.md b/README.md index 936f6b1..33e9150 100644 --- a/README.md +++ b/README.md @@ -1,161 +1,22 @@ -

- - IPFS in JavaScript logo - -

+Based heavily on the [browser video streaming](https://github.com/ipfs-examples/js-ipfs-examples/tree/master/examples/browser-video-streaming) example from JS-IPFS. -

js-ipfs video streaming with hls.js

+Accepts three query parameters: -

- Streaming video in the browser with js-ipfs and hls.js -
-
- -
- Explore the docs - · - View Demo - · - Report Bug - · - Request Feature/Example -

+- `hash`: required. The IPFS hash of a folder containing an HLS playlist and its files. +- `source`: optional, defaults to `master.m3u8`. +- `title`: optional, allows overriding the browser tab title. -## Table of Contents +Here are commands you can use to encode and upload a video to IPFS: -- [Table of Contents](#table-of-contents) -- [About The Project](#about-the-project) -- [Getting Started](#getting-started) - - [Prerequisites](#prerequisites) - - [Installation and Running example](#installation-and-running-example) -- [Usage](#usage) - - [Why use HLS?](#why-use-hls) - - [hlsjs-ipfs-loader](#hlsjs-ipfs-loader) - - [Generating HLS content](#generating-hls-content) -- [References](#references) -- [Documentation](#documentation) -- [Contributing](#contributing) -- [Want to hack on IPFS?](#want-to-hack-on-ipfs) - -## About The Project - -- Read the [docs](https://github.com/ipfs/js-ipfs/tree/master/docs) -- Look into other [examples](https://github.com/ipfs-examples/js-ipfs-examples) to learn how to spawn an IPFS node in Node.js and in the Browser -- Consult the [Core API docs](https://github.com/ipfs/js-ipfs/tree/master/docs/core-api) to see what you can do with an IPFS node -- Visit https://dweb-primer.ipfs.io to learn about IPFS and the concepts that underpin it -- Head over to https://proto.school to take interactive tutorials that cover core IPFS APIs -- Check out https://docs.ipfs.io for tips, how-tos and more -- See https://blog.ipfs.io for news and more -- Need help? Please ask 'How do I?' questions on https://discuss.ipfs.io - -## Getting Started - -### Prerequisites - -Make sure you have installed all of the following prerequisites on your development machine: - -- Git - [Download & Install Git](https://git-scm.com/downloads). OSX and Linux machines typically have this already installed. -- Node.js - [Download & Install Node.js](https://nodejs.org/en/download/) and the npm package manager. - -### Installation and Running example - -```console -> npm install -> npm start +``` +mkdir output +cd output +ffmpeg -i ../yourVideoFilename.mp4 -profile:v baseline -level 3.0 -start_number 0 -hls_time 5 -hls_list_size 0 -f hls master.m3u8 +ipfs add -Qr . ``` -Now open your browser at `http://localhost:8888` +The output from the `ipfs` command is the hash to use with this page. -## Usage +Example URL: -This example shows a method for video/audio streaming in the browser over IPFS. - -_Note:_ If you try to run the example straight from disk, some browsers (e.g Chrome) might, for security reasons, prevent some resources from loading correctly. To get around this, simply cd into the directory of this example and use http-server from npm: - -```console -$ npm install -g http-server -$ http-server -``` - -You should then be able to stream Big Buck Bunny by pointing your browser at http://localhost:8080. - -In addition to video streaming, plain audio streaming works fine as well. Simply use the same ffmpeg + ipfs procedure as described above, but with your audio file as input. You may also want to change the video tag to `audio` (video tags will play plain audio as well, but the player looks a bit strange). - -On a final note, without diving too deep into what the specific ffmpeg HLS options above mean, it's worth mentioning the `hls_time` option, which defines the length of each HLS chunk (in seconds) and is potentially interesting for performance tuning (see for example [this article](https://bitmovin.com/mpeg-dash-hls-segment-length/)). - -_For more examples, please refer to the [Documentation](#documentation)_ - -### Why use HLS? - -HLS (Apple's HTTP Live Streaming) is one of several protocols currently available for adaptive bitrate streaming. - -One of the advantages of HLS over some other streaming technologies is that the content can be hosted on a plain old web server without any special server-side support. The way this works is that the original content (the stream or video/audio file) is split up into small MPEG2-TS segments before being uploaded to the server. The segments are then fetched by the HLS player on the fly (using regular HTTP GET requests) and get spliced together to a continuous stream. - -In addition to the segments there are also so-called manifests (m3u8 files) which contain metadata about segments and their bitrates. A stream can contain segments of multiple bitrates and the HLS player will automatically switch to the optimal bitrate based on client performance. - -The fact that HLS content is just "a bunch of files" makes it a good choice for IPFS (another protocol that works this way is MPEG-DASH, which could certainly be a good choice as well). Furthermore, the [hls.js](https://github.com/video-dev/hls.js) library enables straightforward integration with the HTML5 video element. - -### hlsjs-ipfs-loader - -The hls.js library ships with an HTTP based content loader only, but it's fortunately possible to configure custom content loaders as well, which is what makes IPFS streaming possible in this case. A loader implementation that fetches content using js-ipfs can be found [here](https://www.npmjs.com/package/hlsjs-ipfs-loader), and is easy to use on a regular HTML page: - -```html - - -``` - -### Generating HLS content - -In order for any of the above to be useful, we also need to have a way to actually generate HLS manifests and MPEG2-TS segments from an arbitrary video/audio file. Luckily, most new builds of `ffmpeg` are compiled with this capability. - -For example, say we have a directory containing a video file `BigBuckBunny_320x180.mp4`. We can then create a sub directory and generate the HLS data there, and finally add it to IPFS: - -```console -$ mkdir hls-bunny -$ cd hls-bunny -$ ffmpeg -i ../BigBuckBunny_320x180.mp4 -profile:v baseline -level 3.0 -start_number 0 -hls_time 5 -hls_list_size 0 -f hls master.m3u8 -$ ipfs add -Qr . -``` - -The most important piece of information to note down is the name you choose for the HLS manifest (master.m3u8 in this example, but you're free to use any name), and the hash returned by `ipfs add`. Consult [streaming.js](streaming.js) for a full example of how these values are used. - -## References - -- Documentation: - - [IPFS CONFIG](https://github.com/ipfs/js-ipfs/blob/master/docs/CONFIG.md) - - [MISCELLANEOUS](https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/MISCELLANEOUS.md) - -## Documentation - -- [Config](https://docs.ipfs.io/) -- [Core API](https://github.com/ipfs/js-ipfs/tree/master/docs/core-api) -- [Examples](https://github.com/ipfs-examples/js-ipfs-examples) -- [Development](https://github.com/ipfs/js-ipfs/blob/master/docs/DEVELOPMENT.md) -- [Tutorials](https://proto.school) - -## Contributing - -Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. - -1. Fork the IPFS Project -2. Create your Feature Branch (`git checkout -b feature/amazing-feature`) -3. Commit your Changes (`git commit -a -m 'feat: add some amazing feature'`) -4. Push to the Branch (`git push origin feature/amazing-feature`) -5. Open a Pull Request - -## Want to hack on IPFS? - -[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) - -The IPFS implementation in JavaScript needs your help! There are a few things you can do right now to help out: - -Read the [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md) and [JavaScript Contributing Guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md). - -- **Check out existing issues** The [issue list](https://github.com/ipfs/js-ipfs/issues) has many that are marked as ['help wanted'](https://github.com/ipfs/js-ipfs/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22help+wanted%22) or ['difficulty:easy'](https://github.com/ipfs/js-ipfs/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Adifficulty%3Aeasy) which make great starting points for development, many of which can be tackled with no prior IPFS knowledge -- **Look at the [IPFS Roadmap](https://github.com/ipfs/roadmap)** This are the high priority items being worked on right now -- **Perform code reviews** More eyes will help - a. speed the project along - b. ensure quality, and - c. reduce possible future bugs. -- **Add tests**. There can never be enough tests. -- **Join the [Weekly Core Implementations Call](https://github.com/ipfs/team-mgmt/issues/992)** it's where everyone discusses what's going on with IPFS and what's next +http://ipfs.io/ipfs/Qma8m84bDQGfSPEZreNg4ZKKTXJAbcFsrkcjgajGmXkXjs/index.html?hash=QmdpAidwAsBGptFB3b6A9Pyi5coEbgjHrL3K2Qrsutmj9K&title=Big%20Buck%20Bunny \ No newline at end of file diff --git a/dist/favicon.5c4f16e7.ico b/dist/favicon.5c4f16e7.ico deleted file mode 100644 index b2f1f9683f0def6d7d845f6da337ffe78ac63831..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34494 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 numeric require -// -// anything defined in a previous bundle is accessed via the -// orig method which is the require for previous bundles - -(function (modules, entry, mainEntry, parcelRequireName, globalName) { - /* eslint-disable no-undef */ - var globalObject = - typeof globalThis !== 'undefined' - ? globalThis - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' - ? window - : typeof global !== 'undefined' - ? global - : {}; - /* eslint-enable no-undef */ - - // Save the require from previous bundle to this closure if any - var previousRequire = - typeof globalObject[parcelRequireName] === 'function' && - globalObject[parcelRequireName]; - - var cache = previousRequire.cache || {}; - // Do not use `require` to prevent Webpack from trying to bundle this call - var nodeRequire = - typeof module !== 'undefined' && - typeof module.require === 'function' && - module.require.bind(module); - - function newRequire(name, jumped) { - if (!cache[name]) { - if (!modules[name]) { - // if we cannot find the module within our internal map or - // cache jump to the current global require ie. the last bundle - // that was added to the page. - var currentRequire = - typeof globalObject[parcelRequireName] === 'function' && - globalObject[parcelRequireName]; - if (!jumped && currentRequire) { - return currentRequire(name, true); - } - - // If there are other bundles on this page the require from the - // previous one is saved to 'previousRequire'. Repeat this as - // many times as there are bundles until the module is found or - // we exhaust the require chain. - if (previousRequire) { - return previousRequire(name, true); - } - - // Try the node require function if it exists. - if (nodeRequire && typeof name === 'string') { - return nodeRequire(name); - } - - var err = new Error("Cannot find module '" + name + "'"); - err.code = 'MODULE_NOT_FOUND'; - throw err; - } - - localRequire.resolve = resolve; - localRequire.cache = {}; - - var module = (cache[name] = new newRequire.Module(name)); - - modules[name][0].call( - module.exports, - localRequire, - module, - module.exports, - this - ); - } - - return cache[name].exports; - - function localRequire(x) { - return newRequire(localRequire.resolve(x)); - } - - function resolve(x) { - return modules[name][1][x] || x; - } - } - - function Module(moduleName) { - this.id = moduleName; - this.bundle = newRequire; - this.exports = {}; - } - - newRequire.isParcelRequire = true; - newRequire.Module = Module; - newRequire.modules = modules; - newRequire.cache = cache; - newRequire.parent = previousRequire; - newRequire.register = function (id, exports) { - modules[id] = [ - function (require, module) { - module.exports = exports; - }, - {}, - ]; - }; - - Object.defineProperty(newRequire, 'root', { - get: function () { - return globalObject[parcelRequireName]; - }, - }); - - globalObject[parcelRequireName] = newRequire; - - for (var i = 0; i < entry.length; i++) { - newRequire(entry[i]); - } - - if (mainEntry) { - // Expose entry point to Node, AMD or browser globals - // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js - var mainExports = newRequire(mainEntry); - - // CommonJS - if (typeof exports === 'object' && typeof module !== 'undefined') { - module.exports = mainExports; - - // RequireJS - } else if (typeof define === 'function' && define.amd) { - define(function () { - return mainExports; - }); - - //