From fc5c72f0b7e958e1fcca399a05b1332d1f95b224 Mon Sep 17 00:00:00 2001 From: PierreDillard <7gaspard77@gmail.com> Date: Mon, 5 Aug 2024 10:05:31 +0200 Subject: [PATCH 01/28] updates levels, keywords-cloud, modal --- .gitignore | 2 + docs/Howtos/encoding.md | 8 +- docs/Howtos/filters-oneliners.md | 38 ++-- docs/Howtos/gpac-mp4box.md | 2 +- docs/Howtos/inspecting.md | 8 +- docs/Howtos/mp4box-filters.md | 10 +- docs/Howtos/mp4box-inplace.md | 6 +- docs/Howtos/network-capture.md | 4 +- docs/Howtos/nodejs.md | 14 +- docs/Howtos/pipes.md | 6 +- docs/Howtos/playlist.md | 6 +- docs/Howtos/raw-formats.md | 8 +- docs/Howtos/sockets.md | 2 +- docs/data/keywords.json | 142 +++++++++++++ docs/glossary.md/encode.md | 35 ++++ docs/glossary.md/encrypt.md | 39 ++++ docs/javascripts/cache.js | 8 + docs/javascripts/domManipulation.js | 119 +++++++++++ docs/javascripts/fetchFunctions.js | 59 ++++++ docs/javascripts/keywordsDisplay.js | 51 +++++ docs/javascripts/keywordsFinder.js | 30 +++ docs/javascripts/levels.js | 173 ++++++++++++++++ docs/javascripts/main.js | 24 +++ docs/javascripts/modalFunctions.js | 55 +++++ docs/stylesheets/collapse_section.css | 103 +++++++++ docs/stylesheets/extra.css | 143 ++++++++----- docs/stylesheets/feedback.css | 4 + docs/stylesheets/glossary-pages.css | 6 + docs/stylesheets/keyword-cloud.css | 121 +++++++++++ docs/stylesheets/levels.css | 84 ++++++++ docs/stylesheets/modal.css | 83 ++++++++ docs/stylesheets/toggle.css | 69 +++++++ docs/stylesheets/top.css | 32 +++ mkdocs.yml | 48 ++++- overrides/base.html | 276 +++++++++++++++++++++++++ overrides/main.html | 4 + overrides/partials/header.html | 76 +++++++ overrides/partials/keywords-cloud.html | 31 +++ overrides/partials/nav-items.html | 152 ++++++++++++++ overrides/partials/nav.html | 31 +++ overrides/partials/search.html | 44 ++++ overrides/partials/toc-item.html | 19 ++ overrides/partials/toc.html | 28 +++ overrides/partials/top.html | 8 + 44 files changed, 2100 insertions(+), 111 deletions(-) create mode 100644 docs/data/keywords.json create mode 100644 docs/glossary.md/encode.md create mode 100644 docs/glossary.md/encrypt.md create mode 100644 docs/javascripts/cache.js create mode 100644 docs/javascripts/domManipulation.js create mode 100644 docs/javascripts/fetchFunctions.js create mode 100644 docs/javascripts/keywordsDisplay.js create mode 100644 docs/javascripts/keywordsFinder.js create mode 100644 docs/javascripts/levels.js create mode 100644 docs/javascripts/main.js create mode 100644 docs/javascripts/modalFunctions.js create mode 100644 docs/stylesheets/collapse_section.css create mode 100644 docs/stylesheets/feedback.css create mode 100644 docs/stylesheets/glossary-pages.css create mode 100644 docs/stylesheets/keyword-cloud.css create mode 100644 docs/stylesheets/levels.css create mode 100644 docs/stylesheets/modal.css create mode 100644 docs/stylesheets/toggle.css create mode 100644 docs/stylesheets/top.css create mode 100644 overrides/base.html create mode 100644 overrides/main.html create mode 100644 overrides/partials/header.html create mode 100644 overrides/partials/keywords-cloud.html create mode 100644 overrides/partials/nav-items.html create mode 100644 overrides/partials/nav.html create mode 100644 overrides/partials/search.html create mode 100644 overrides/partials/toc-item.html create mode 100644 overrides/partials/toc.html create mode 100644 overrides/partials/top.html diff --git a/.gitignore b/.gitignore index 8f997d18..ecb5b0f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .ignore/ site/ .venv/ +venv/ __pycache__/ site.* .DS_Store +node_modules/ diff --git a/docs/Howtos/encoding.md b/docs/Howtos/encoding.md index e533a029..58765026 100644 --- a/docs/Howtos/encoding.md +++ b/docs/Howtos/encoding.md @@ -1,4 +1,4 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to use encoders in GPAC. @@ -6,7 +6,7 @@ GPAC filter graph resolution always targets the shortest possible path between t For example, if the source is an AAC file and the destination is an ISOBMFF file, since ISOBMFF accepts AAC data inputs, no encoder will be used. But if the source is an AAC file and the destination is an MP3 file, the graph resolver will load a decoder and an MP3 encoder to move source data from AAC to MP3. -# Encoding Video +# Encoding Video {: data-level="beginner" } ## Encoding from a raw video file @@ -99,7 +99,7 @@ You can use the [ffsws](ffsws) filter to rescale videos in your pipeline: This will resize (downscale or upscale) input to a resolution of 1280x720 without checking aspect ratio, and encode to AVC at 1 mbps. To keep aspect ratio, use `ffsws:osize=1280x720:keepar=full`. -# Encoding Audio +# Encoding Audio {: data-level="beginner" } ## Encoding from files This is basically the same as video @@ -120,7 +120,7 @@ You can use the [audio resampler](resample) filter to change your input signal c This will resample the input signal to stereo 48000 Hz and encode to AAC at 128k. -# Transcoding AV file +# Transcoding AV file {: data-level="beginner" } Combine the above tow steps: diff --git a/docs/Howtos/filters-oneliners.md b/docs/Howtos/filters-oneliners.md index 958e9d4a..48ac8408 100644 --- a/docs/Howtos/filters-oneliners.md +++ b/docs/Howtos/filters-oneliners.md @@ -1,4 +1,9 @@ -# Foreword + + + + +# Foreword {: data-level="all" } + This page contains one-liners illustrating the many possibilities of GPAC filters architecture. For a more detailed information, it is highly recommended that you read: @@ -13,7 +18,7 @@ To get a better understanding of each command illustrated in this case, it is re Whenever an option is specified, e.g. `dest.mp4:foo`, you can get more info and locate the parent filter of this option using `gpac -h foo`. The filter session is by default quiet, except for warnings and error reporting. To get information on the session while running, use [-r](gpac_general#r) option. To get more runtime information, use the [log system](core_logs). - +sectionLevel === null || Given the configurable nature of the filter architecture, most examples given in one context can be reused in another context. For example: - from the dump examples: @@ -45,7 +50,7 @@ GPAC filters can use either: _NOTE This page is under permanent construction, feel free to contribute !_ -# Source Inspection +# Source Inspection {: data-level="beginner" } Check if a source is supported and get quick information on the media streams - _see [filter](inspect), [howto](inspecting)_ ``` @@ -95,7 +100,7 @@ Count all AC3 audio streams in a source - _see [filter](probe), [doc](filters_g res = `gpac -i source @#CodecID=ac3 probe` ``` -# Dumping and decoding +# Dumping and decoding Extract AVC track, potentially transcoding - _see [filter](writegen)_ ``` @@ -147,7 +152,7 @@ Extract all AAC audio tracks - _see [doc](filters_general#complex-links)_ gpac -i source reframer @#CodecID=aac -o dump_$ID$:dynext ``` -# Multiplexing +# Multiplexing {: data-level="beginner" } Mux sources to MP4 - _see [filter](mp4mx)_ ``` @@ -192,7 +197,7 @@ Mux sources to MKV (for builds with FFmpeg support) - _see [filter](ffmx)_ gpac -i source -o dst.mkv ``` -# Remultiplexing +# Remultiplexing {: data-level="beginner" } Remux sources to MP4 forcing bitstream reparsing (all syntaxes are equivalent) ``` @@ -201,7 +206,7 @@ MP4Box -add source:unframer -new dst.mp4 ``` -# Encoding +# Encoding {: data-level="beginner" } Encode an MP3 to an AAC file at 100 kbps - _see [filter](ffenc)_ ``` @@ -258,7 +263,7 @@ gpac -i source enc:c=avc:b=3m:pass2 -o dst.mp4 ``` -# Rescaling +# Rescaling rescale without respecting aspect ratio ``` @@ -276,7 +281,7 @@ gpac -i source1.mp4 ffsws:osize=510x512:osr=3/2 vout ``` -# Encryption +# Encryption Encrypt and dash several sources using a single drm configuration - _see [filter](cecrypt)_ ``` @@ -294,7 +299,7 @@ gpac -i source.mp4 dasher:gencues cecrypt:cfile=roll_seg.xml -o live.mpd ``` -# Piping and sockets +# Piping and sockets Grab a compressed stream (e.g. AVC|H264) from stdin, remove all non I-frames and non-video PIDs and output as raw 264 over stdout - _see [filter](fout) [howto](pipes)_ ``` @@ -402,7 +407,7 @@ gpac -i source::#HLSMExt=vfoo,vbar=video::#HLSVExt=#fooVideo,#bar1=optVideo -i s ``` -# Time modification +# Time modification _Note: If the source is an MP4 file, it is much simpler/faster to perform these operations using MP4Box_ @@ -427,7 +432,7 @@ gpac -i source restamp:fps=30000/1001:rawv=force -o dst ``` -# Source splitting +# Source splitting {: data-level="beginner" } Extract from 1min to 2min30s - _see [filter](restamp)_ ``` @@ -457,7 +462,7 @@ gpac -i source reframer:xs=T00:01:00,T00:05:20:xe=T00:02:30 -o dst ``` -# Playlists and source concatenation +# Playlists and source concatenation {: data-level="beginner" } Loop file forever playback - _see [filter](flist)_ ``` @@ -483,7 +488,7 @@ ls *.mp4 > pl.m3u gpac -i pl.m3u vout ``` -# Playback +# Playback { : data-level="beginner" } Basic playback - _see [howto](filters-playback)_ ``` @@ -554,7 +559,7 @@ Decode source MP4 enabling only the minimum filters: gpac -blacklist=-fin,mp4dmx,ffdec,vout source.mp4 vout ``` -# FFmpeg support +# FFmpeg support Set gpac as RTMP output server ``` @@ -771,5 +776,4 @@ Fractal-like animated video reuse ] } ] -``` - +``` \ No newline at end of file diff --git a/docs/Howtos/gpac-mp4box.md b/docs/Howtos/gpac-mp4box.md index 69173f31..6305e497 100644 --- a/docs/Howtos/gpac-mp4box.md +++ b/docs/Howtos/gpac-mp4box.md @@ -1,4 +1,4 @@ -# MP4Box vs gpac +# MP4Box vs gpac {: data-level="all" } Following the introduction of the filter architecture and the gpac application, you may have a hard time choosing between MP4Box and gpac. diff --git a/docs/Howtos/inspecting.md b/docs/Howtos/inspecting.md index 6088c80d..3519ba39 100644 --- a/docs/Howtos/inspecting.md +++ b/docs/Howtos/inspecting.md @@ -1,9 +1,9 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to use the [inspect](inspect) filter to get information on sources in GPAC. -# Media Streams inspection +# Media Streams inspection {: data-level="beginner" } ``` gpac -i source.mp4 inspect @@ -55,6 +55,7 @@ The start and duration of the inspection can be modified: ``` gpac -i source.mp4 inspect:deep:start=10:dur=1 ``` + This will force inspecting from start time 10 second (or previous access point) for 1 second. # Filtering the inspection @@ -76,6 +77,7 @@ The full list of options for the packet information log is given in the [inspect ``` gpac -i source.mp4 inspect:interleave=false:fmt="PCK%num% DTS=%dts% CTS=%cts% SAP=%sap% size=%size%%lf%":log=dump.txt ``` + This is the same as above with some nicer formatting of the output: ``` PCK1 DTS=0 CTS=0 SAP=1 size=200 @@ -111,4 +113,4 @@ Using the mode `analyse=bs` will give, for supported media types, the fields rea This is still a work in progress, more media types need to be added and deeper analysis of the packets should be done (for now only the slice headers are parsed). -This should however help you check consistency of systems layer information (timing, SAP, dependency information) with frame properties. +This should however help you check consistency of systems layer information (timing, SAP, dependency information) with frame properties. \ No newline at end of file diff --git a/docs/Howtos/mp4box-filters.md b/docs/Howtos/mp4box-filters.md index b9511047..c0b7b904 100644 --- a/docs/Howtos/mp4box-filters.md +++ b/docs/Howtos/mp4box-filters.md @@ -1,4 +1,4 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to use the [MP4Box](MP4Box-introduction) together with filters in GPAC. @@ -12,9 +12,9 @@ As discussed [here](Rearchitecture), the following features of MP4Box are now us At the time being, only media importing and DASHing operations can be extended to use other filters. -# Media Importing +# Media Importing -# Customizing source and mux parameters +# Customizing source and mux parameters {: data-level="beginner" } It is possible to provide additional parameters to both source and destination during an import operations. Most MP4Box parameters are mapped to the filter engine and they have roughly the same names, but the opposite is not true (i.e. most options of the various filters in GPAC are not mapped as MP4Box options). - source options are declared using `:sopt:` separator @@ -53,7 +53,7 @@ The filter statistics can be seen by using the `fstat` option: MP4Box -add source.aac:fstat -new file.mp4 ``` -# Filtering input streams +# Filtering input streams {: data-level="beginner" } Assume you have an input file with several tracks/media streams, such as two videos in AVC and HEVC, and audios in english and french, and you only want to import AVC and english audio from that source file. With regular MP4Box usage, you would need to know the track IDs of the desired tracks. If you have a collection of such files with varying track IDs, you would need complex dumping and analyzing of the file tracks to extract their track IDs and import them. @@ -125,7 +125,7 @@ You may ask yourself whether using MP4Box or gpac is more efficient for such an - The filter architecture does not support (for the moment) reading and writing in the same file, so if you need to add a track to an existing file, you must use MP4Box for that. -# DASHing +# DASHing {: data-level="beginner" } It is possible to provide a filter chain to each source being DASHed with MP4Box. diff --git a/docs/Howtos/mp4box-inplace.md b/docs/Howtos/mp4box-inplace.md index dea65ac0..9c891f31 100644 --- a/docs/Howtos/mp4box-inplace.md +++ b/docs/Howtos/mp4box-inplace.md @@ -1,4 +1,4 @@ -# Overview +# Overview {: data-level="all" } As of GPAC 2.0, MP4Box supports in-place editing of MP4 files. @@ -32,14 +32,14 @@ MP4Box -ab TEST -itags artist=GPAC movie.mp4 Tests for in-place storage are available [here](https://github.com/gpac/testsuite/blob/master/scripts/mp4box-inplace.sh). -# Flat storage files +# Flat storage files {: data-level="all" } In files stored with flat storage (`-flat` in MP4Box), the media data is placed before the structured data (`moov` and `meta` box) and usually does not need any shifting. There is one exception to this: when adding brands, since they must be located first in the file, it is necessary to shift the media data. There is currently no way to reserve space for future brand edition in MP4Box, and any brand add operation will result in media data shift. We therefore recommend using interleaved storage (`moov`/ `meta` first). -# Interleaved files +# Interleaved files {: data-level="all" } In files stored with interleaved storage (`-inter` in MP4Box), the media data is placed after the structured data (`moov` and `meta` box) and may need shifting whenever the size of {ftyp, moov, meta} boxes changes. In order to avoid shifting when this size decreases, a `free` box is inserted before the media data. When this size increases, the media data is shifted if writing the structured data will overwrite the start of the media data. Otherwise, a `free` box is written between the `moov+meta` boxes and the media data. diff --git a/docs/Howtos/network-capture.md b/docs/Howtos/network-capture.md index f8807fab..ccaa1fa0 100644 --- a/docs/Howtos/network-capture.md +++ b/docs/Howtos/network-capture.md @@ -1,8 +1,8 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to use network captures with GPAC 2.3-DEV or above. -# Overview + GPAC can: diff --git a/docs/Howtos/nodejs.md b/docs/Howtos/nodejs.md index 5e69a92b..c20dc782 100644 --- a/docs/Howtos/nodejs.md +++ b/docs/Howtos/nodejs.md @@ -1,4 +1,4 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to use [GPAC Filters](Filters) in NodeJS. @@ -11,7 +11,7 @@ You can also have a look at the [test script](https://github.com/gpac/gpac/tree __Warning GPAC NodeJS bindings are only available starting from GPAC 2.0.__ -# Before you begin +# Before you begin {: data-level="beginner" } The GPAC NodeJS bindings are using [n-api](https://nodejs.org/api/n-api.html) for interfacing with libgpac filter session, while providing an object-oriented wrapper hiding all GPAC C design. @@ -82,7 +82,7 @@ Running this should print your current GPAC version. A test program [gpac.js](https://github.com/gpac/gpac/blob/master/share/nodejs/test/gpac.js) exercising most of the NodeJS GPAC bindings is available in `gpac/share/nodejs/test` -# Tuning up GPAC +# Tuning up GPAC {: data-level="beginner" } The first thing to do is to initialize libgpac. This is done by default while importing the bindings with the following settings: @@ -117,7 +117,7 @@ gpac.set_logs("dash@info"); ``` -# Setting up filter sessions +# Setting up filter sessions {: data-level="beginner" } ## Simple sessions To create a filter session, the simplest way is to use all defaults value, creating a single-threaded blocking session: @@ -254,7 +254,7 @@ Note that (as in GPAC JS or Python) properties referring to constant values are - AudioFormat: string containing the audio format name -# Custom Filters +# Custom Filters {: data-level="expert" } You can define your own filter(s) to interact with the media pipeline. As usual in GPAC filters, a custom filter can be a source, a sink or any other filter. It can consume packets from input PIDs and produce packets on output PIDs. @@ -437,7 +437,7 @@ fs.run(); -# Custom GPAC callbacks +# Custom GPAC callbacks {: data-level="expert" } Some callbacks from libgpac are made available in NodeJS ## Remotery interaction @@ -716,7 +716,7 @@ _NOTE When running the session in multi-thread mode, file IO callbacks are alway -# Multithread support +# Multithread support {: data-level="expert" } Multithreaded filter sessions can be used with NodeJS, however the binding currently only supports executing callbacks into NodeJS from the main thread (main NodeJS or worker). diff --git a/docs/Howtos/pipes.md b/docs/Howtos/pipes.md index d2642abb..6d1f49e3 100644 --- a/docs/Howtos/pipes.md +++ b/docs/Howtos/pipes.md @@ -1,10 +1,10 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to use pipes in GPAC. GPAC supports data piping on Linux, OSX and Windows. The pipe is unidirectional, and can be used as source or as destination. -# Input pipe +# Input pipe {: data-level="beginner" } ## Simple reception of data @@ -36,7 +36,7 @@ The above command will run forever, since broken pipe messages are ignored. You There is currently no way to signal from the sender that the session should be closed, we might add this feature in the near future. -# Output pipe +# Output pipe Assume you have an app that consumes AVC|H264 in Annex B format from a pipe `myavcpipe`. You can direct GPAC output to this pipe: diff --git a/docs/Howtos/playlist.md b/docs/Howtos/playlist.md index 05a23824..82c0468e 100644 --- a/docs/Howtos/playlist.md +++ b/docs/Howtos/playlist.md @@ -1,4 +1,4 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to use the [flist](flist) filter to deal with playlists in GPAC. @@ -10,7 +10,7 @@ In both modes, when switching sources, the filter will match streams (PIDs) base The filter will move to the next item once all PIDs are done playing. It will then adjust the timeline of the following source by repositioning the new source smallest initial timestamp to the greatest time (timestamp+duration) of the last source. -# File list mode +# File list mode {: data-level="beginner" } ``` gpac flist:srcs=file.mp4:floop=-1 vout @@ -32,7 +32,7 @@ gpac flist:srcs=images/*.png:fdur=1:fsort=date -o slide.mp4 ``` The above command will gather all files with extension `png` in directory `images` ordered by their file creation date, each image lasting for 1 second, and output as a PNG track in MP4 format. -# Playlist mode +# Playlist mode ## General usage The playlist mode allows you to build complex source sequences. Think of it as piping the output of several sequential `gpac` executions into a consuming `gpac` instance. diff --git a/docs/Howtos/raw-formats.md b/docs/Howtos/raw-formats.md index 2adbdb7b..8070e72b 100644 --- a/docs/Howtos/raw-formats.md +++ b/docs/Howtos/raw-formats.md @@ -1,9 +1,9 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to work with RAW, uncompressed audio and video data in GPAC. -# RAW Video +# RAW Video {: data-level="beginner" } ## Extracting raw video from a file @@ -89,7 +89,7 @@ This will resize and extract the video frames from start time 10s until end rang The above command will load a raw YUV420 planar 8-bits file using a resolution of 128x128 pixels, and display it using the [video output](vout) filter. The default frame rate is 25, as indicated in [the raw video reframer](rfrawvid). -# RAW Audio +# RAW Audio {: data-level="beginner" } This is very similar to raw video. ## Extracting raw audio from a file @@ -109,7 +109,7 @@ The above command will dump the audio content from `source.mp4` into a 16-bit li The above command will load a raw 16-bit little endian PCM file using 44100 Hz sample rate, and play it using the [audio output](aout) filter. The default channel count is 2, as indicated in [the raw audio reframer](rfpcm). -# RAW Audio and Video +# RAW Audio and Video {: data-level="beginner" } ## Direct extraction ```gpac -i source.mp4 -o test.pcm -o test.yuv``` diff --git a/docs/Howtos/sockets.md b/docs/Howtos/sockets.md index 86750e59..89241790 100644 --- a/docs/Howtos/sockets.md +++ b/docs/Howtos/sockets.md @@ -1,4 +1,4 @@ -# Overview +# Overview {: data-level="all" } We discuss here how to use sockets in GPAC for generic IO. diff --git a/docs/data/keywords.json b/docs/data/keywords.json new file mode 100644 index 00000000..91a9e283 --- /dev/null +++ b/docs/data/keywords.json @@ -0,0 +1,142 @@ + +{ + "definitions": { + "SOURCE": { + "description": "In GPAC, a source is an input element that provides media data to be processed. It is typically the starting point of a media processing pipeline.", + "level": "beginner" + }, + "SINK": { + "description": "A sink in GPAC is an output element that receives processed media data. It is usually the endpoint of a media processing pipeline.", + "level": "beginner" + }, + "INPUT": { + "description": "An input refers to any data fed into the GPAC processing system, including video, audio, and other multimedia streams.", + "level": "beginner" + }, + "OUTPUT": { + "description": "Output in GPAC refers to the processed data that is generated after media processing, ready for storage or display.", + "level": "beginner" + }, + "PID": { + "description": "PID stands for Packet Identifier. It is used in GPAC to uniquely identify different streams within a media file, such as audio, video, and subtitles.", + "level": "expert" + }, + "PACKETS": { + "description": "Packets are units of data transmitted over a network or stored in a media file. In GPAC, packets are processed to extract media streams.", + "level": "beginner" + }, + "DATA": { + "description": "Data in GPAC refers to the raw media content, including video frames, audio samples, and subtitles, that is processed and manipulated.", + "level": "beginner" + }, + "STREAM": { + "description": "A stream is a sequence of media data (audio, video, etc.) that is processed by GPAC. Streams are identified by PIDs and can be multiplexed or demultiplexed.", + "level": "beginner" + }, + "MEDIA": { + "description": "Media refers to various types of content such as video, audio, and images. GPAC processes different media types to enable playback, editing, and streaming.", + "level": "beginner" + }, + "FRAME": { + "description": "A frame is a single image in a sequence of images that make up a video. GPAC processes frames during video encoding and decoding.", + "level": "beginner" + }, + "OPTION": { + "description": "Options in GPAC are parameters that configure the behavior of filters and processing tasks. They can be set to customize media processing.", + "level": "beginner" + }, + "PROPERTY": { + "description": "Properties in GPAC refer to attributes of media streams or filters, such as resolution, bitrate, and codec type, that affect processing.", + "level": "all" + }, + "CODEC": { + "description": "A codec is a software or hardware tool that encodes or decodes media data. GPAC supports various codecs for compressing and decompressing media streams.", + "level": "beginner" + }, + "SESSION": { + "description": "A session in GPAC is a context for managing the lifecycle of media processing tasks, including loading, configuring, and executing filters.", + "level": "expert" + }, + "LINK": { + "description": "Links in GPAC connect different filters and processing elements, forming a processing chain or graph. They facilitate the flow of media data.", + "level": "beginner" + }, + "CONNECTIONS": { + "description": "Connections in GPAC refer to the relationships between filters in a processing graph. They define how data flows from one filter to another.", + "level": "beginner" + }, + "CHAIN": { + "description": "A chain in GPAC is a sequence of connected filters that process media data in a specific order. Chains define the processing pipeline.", + "level": "beginner" + }, + "GRAPH": { + "description": "A graph in GPAC represents the entire media processing workflow, consisting of nodes (filters) and edges (connections) that define data flow.", + "level": "all" + }, + "DECODING": { + "description": "Decoding is the process of converting compressed media data back into its original format. GPAC uses various codecs to decode audio and video streams.", + "level": "beginner" + }, + "REFRAMER": { + "description": "A reframer in GPAC adjusts the timing and structure of media frames to meet specific requirements, such as alignment for streaming.", + "level": "expert" + }, + + "ENCRYPT": { + "description": "Encryption in GPAC refers to the process of securing media content by converting it into a code to prevent unauthorized access. GPAC supports various encryption standards like CENC and ISMA.", + "level": "expert" + }, + "ENCODE": { + "description": "Encoding in GPAC is the process of converting raw media data into a compressed format using codecs. This is essential for reducing file sizes and preparing media for streaming or storage.", + "level": "all" + }, + "MP4": { + "description": "MP4 is a standardized multimedia file format for storing video, audio, subtitles and still images. It is widely used for streaming and media storage.", + "level": "beginner" + }, + + "DASH": { + "description": "DASH stands for Dynamic Adaptive Streaming over HTTP, a technique that enables high-quality streaming of media content over the internet.", + "level": "all" + } + , + "SCENE": { + "description": "Scene in GPAC refers to a multimedia presentation, including video, audio, and interactive elements, typically described using BIFS or X3D.", + "level": "all" + }, + "MPD": { + "description": "MPD stands for Media Presentation Description, a file that provides metadata about the media content in a DASH session.", + "level": "beginner" + }, + + "BOX": { + "description": "A box in GPAC refers to a container or structure within a media file format, such as an MP4 box, used to store metadata and media data.", + "level": "beginner" + } + , + "RAW": { + "description": "Raw in GPAC refers to uncompressed media data that has not been processed or encoded.", + "level": "beginner" + }, + "GROUP": { + "description": "A group in GPAC refers to a collection of related items, such as tracks or filters, that are processed or managed together.", + "level": "beginner" + }, + "PROFILE": { + "description": "A profile in GPAC refers to a set of predefined settings or configurations for media processing tasks, such as encoding profiles.", + "level": "beginner" + } + , + "XML": { + "description": "XML stands for eXtensible Markup Language, used in GPAC for storing and exchanging metadata and configuration settings.", + "level": "all" + } + , + "TILE": { + "description": "A tile in GPAC refers to a rectangular section of a video frame, used in tiling and parallel processing of video content.", + "level": "all" + } + + + } +} \ No newline at end of file diff --git a/docs/glossary.md/encode.md b/docs/glossary.md/encode.md new file mode 100644 index 00000000..a7c3f45d --- /dev/null +++ b/docs/glossary.md/encode.md @@ -0,0 +1,35 @@ + +`encode` is a function that allows you to encode multimedia files into various formats using specified codecs. + +## Reference + +### +```bash +encode(input_file, output_file, codec)` +``` +## Usage + +- **Encoding video files** +- **Encoding audio files** +- **Transcoding multimedia streams to different formats** +- **Setting encoding parameters such as bitrate and resolution** + +## Troubleshooting + +### The output file is not playing correctly +- Verify that the codec specified is compatible with the input multimedia file. + +### Encoding is very slow +- Ensure your system has sufficient resources and consider reducing the quality or complexity of the encoding settings. + +## Example + +```bash +encode("input.mp4", "output.mp4", "libx264") +``` + +## Parameters + +- **input_file**: Path to the multimedia file to be encoded. +- **output_file**: Path where the encoded file will be saved. +- **codec**: Codec to be used for encoding (e.g., libx264 for H.264 encoding). \ No newline at end of file diff --git a/docs/glossary.md/encrypt.md b/docs/glossary.md/encrypt.md new file mode 100644 index 00000000..69cf5a7b --- /dev/null +++ b/docs/glossary.md/encrypt.md @@ -0,0 +1,39 @@ + + + +`encrypt` is a function that allows you to encrypt multimedia files using specified encryption keys. + +```bash +encrypt(input_file, output_file, key_file) +``` + +#### Reference +```bash +encrypt("input.mp4", "output.mp4", "keyfile.xml") +``` + +#### Usage + +- Encrypting video files +- Encrypting audio files +- Encrypting live multimedia streams +- Managing encryption keys + +#### Troubleshooting + +- **My output file is corrupted after encryption** + - Verify that the key file is correct and compatible with the input multimedia file. +- **Permission error when accessing the key file** + - Ensure you have the necessary permissions to read the key file. + +#### Example + +```bash +encrypt("input.mp4", "output.mp4", "keyfile.xml") +``` + +#### Parameters + +- **input_file**: Path to the multimedia file to be encrypted. +- **output_file**: Path where the encrypted file will be saved. +- **key_file**: Path to the key file used for encryption. \ No newline at end of file diff --git a/docs/javascripts/cache.js b/docs/javascripts/cache.js new file mode 100644 index 00000000..087a88ff --- /dev/null +++ b/docs/javascripts/cache.js @@ -0,0 +1,8 @@ +function getCache(key) { + let cache = localStorage.getItem(key); + return cache ? JSON.parse(cache) : {}; +} + +function setCache(key, value) { + localStorage.setItem(key, JSON.stringify(value)); +} \ No newline at end of file diff --git a/docs/javascripts/domManipulation.js b/docs/javascripts/domManipulation.js new file mode 100644 index 00000000..1ac9b347 --- /dev/null +++ b/docs/javascripts/domManipulation.js @@ -0,0 +1,119 @@ +//Handle toogle button to switch between NAV and TOC + +document.addEventListener("DOMContentLoaded", function () { + const toggleButton = document.getElementById("toggle-button"); + const tocContent = document.getElementById("toc-content"); + const navContent = document.getElementById("nav-content"); + let isNavIsVisible = true; + + toggleButton.addEventListener("click", function () { + + if (isNavIsVisible) { + navContent.style.display = "none"; + tocContent.style.display = "block"; + } else { + navContent.style.display = "block"; + tocContent.style.display = "none"; + } + isNavIsVisible = !isNavIsVisible; + }); + + tocContent.style.display = "none"; + navContent.style.display = "block"; +}); + +document.addEventListener("DOMContentLoaded", function () { + if (window.location.pathname.includes("/glossary/")) { + document.body.classList.add("glossary-page"); + } +}); + +// Collapse sections + +document.addEventListener("DOMContentLoaded", function () { + + const articleInner = document.querySelector('.md-content__inner'); + const h1Element = articleInner.querySelector('h1'); + const feedbackForm = articleInner.querySelector('.md-feedback'); + + function handleAllSection(section, h2) { + if (section.classList.contains('active')) { + section.setAttribute('data-was-active', 'true'); + } else { + section.removeAttribute('data-was-active'); + } + } + + if (h1Element && feedbackForm) { + + const articleContentDiv = document.createElement('div'); + articleContentDiv.classList.add('article-content'); + + const fragment = document.createDocumentFragment(); + + let sibling = h1Element.nextElementSibling; + while (sibling && sibling !== feedbackForm) { + const nextSibling = sibling.nextElementSibling; + fragment.appendChild(sibling); + sibling = nextSibling; + } + + articleContentDiv.appendChild(fragment); + h1Element.insertAdjacentElement('afterend', articleContentDiv); + } + + const articleContent = document.querySelector('.article-content'); + + if (articleContent) { + const h2Elements = articleContent.querySelectorAll('h2'); + + h2Elements.forEach(h2 => { + const content = []; + let sibling = h2.nextElementSibling; + + while (sibling && sibling.tagName !== 'H2') { + content.push(sibling); + sibling = sibling.nextElementSibling; + } + + let collapseSection = document.createElement('div'); + collapseSection.classList.add('collapse-section'); + + const collapseContent = document.createElement('div'); + collapseContent.classList.add('collapse-content'); + content.forEach(element => collapseContent.appendChild(element)); + + h2.parentNode.insertBefore(collapseSection, h2); + collapseSection.appendChild(h2); + collapseSection.appendChild(collapseContent); + + if (!h2.querySelector('.collapse-icon')) { + const collapseIcon = document.createElement('span'); + collapseIcon.classList.add('collapse-icon'); + collapseIcon.innerHTML = ''; + h2.appendChild(collapseIcon); + } + + h2.addEventListener('click', function () { + collapseSection.classList.toggle('active'); + if (h2.dataset.level === 'all') { + handleAllSection(collapseSection, h2); + } + }); + }); + } +}); +//Handle "all" sections + +function initializeAllSections() { + const allSections = document.querySelectorAll('.collapse-section'); + allSections.forEach(section => { + const h2Element = section.querySelector('h2'); + if (h2Element && h2Element.dataset.level === 'all') { + section.classList.add('active'); + section.setAttribute('data-was-active', 'true'); + } + }); +} + +document.addEventListener("DOMContentLoaded", initializeAllSections); \ No newline at end of file diff --git a/docs/javascripts/fetchFunctions.js b/docs/javascripts/fetchFunctions.js new file mode 100644 index 00000000..7242954e --- /dev/null +++ b/docs/javascripts/fetchFunctions.js @@ -0,0 +1,59 @@ +function fetchKeywords(currentPageMdPath, cachedKeywords, cachedDefinitions) { + fetch('/data/keywords.json') + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + const allDefinitions = data.definitions; + const lexique = Object.keys(allDefinitions); + findKeywordsInContent(currentPageMdPath, lexique, (filteredKeywords) => { + cachedKeywords[currentPageMdPath] = filteredKeywords; + setCache('keywordsCache', cachedKeywords); + const selectedLevel = localStorage.getItem('userLevel') || 'beginner'; + displayKeywords(filteredKeywords, cachedDefinitions, allDefinitions, selectedLevel); + }); + }) + .catch(error => console.error('Error fetching keywords:', error)); +} + +function fetchDefinitions(keyword, cachedDefinitions) { + fetch('/data/keywords.json') + .then(response => response.json()) + .then(data => { + const definition = data.definitions[keyword]; + if (definition) { + cachedDefinitions[keyword] = definition; + setCache('definitionsCache', cachedDefinitions); + openModal(keyword, definition); + } else { + console.error('Definition not found for keyword:', keyword); + } + }) + .catch(error => console.error('Error fetching definition:', error)); +} + +//Get the Markdown content +function fetchMarkdownContent(currentPageMdPath) { + return fetch(currentPageMdPath) + .then(response => { + return response.text(); + }) + .then(htmlContent => { + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlContent, 'text/html'); + const mdContent = doc.querySelector('.md-content[data-md-component="content"]'); + if (mdContent) { + return mdContent.textContent; + } else { + console.warn(`Content element not found in the parsed HTML`); + return ''; + } + }) + .catch(error => { + console.error('Error fetching markdown content:', error); + throw error; + }); +} \ No newline at end of file diff --git a/docs/javascripts/keywordsDisplay.js b/docs/javascripts/keywordsDisplay.js new file mode 100644 index 00000000..e530fd69 --- /dev/null +++ b/docs/javascripts/keywordsDisplay.js @@ -0,0 +1,51 @@ +function displayKeywords(keywords, cachedDefinitions, allDefinitions, selectedLevel) { + const wordCloudElement = document.querySelector('.words-cloud'); + const wordCloudList = document.getElementById('dynamic-words-cloud'); + wordCloudList.innerHTML = ''; + + const sizes = ['size-1', 'size-2', 'size-3', 'size-4', 'size-5']; + const colors = ['color-1', 'color-2', 'color-3', 'color-4']; + + let displayedKeywordsCount = 0; + + const totalRelevantKeywords = keywords.filter(keyword => { + const definition = allDefinitions[keyword]; + return definition && (definition.level === selectedLevel || definition.level === 'all'); + }).length; + + keywords.forEach((keyword, index) => { + const definition = allDefinitions[keyword]; + + if (definition && (definition.level === selectedLevel || definition.level === 'all')) { + displayedKeywordsCount++; + + const li = document.createElement('li'); + const a = document.createElement('a'); + a.href = "#"; + a.textContent = keyword; + a.className = sizes[index % sizes.length] + ' ' + colors[index % colors.length]; + + a.addEventListener('click', function (event) { + event.preventDefault(); + if (cachedDefinitions[keyword]) { + openModal(keyword, cachedDefinitions[keyword]); + } else { + fetchDefinitions(keyword, cachedDefinitions); + } + }); + + li.appendChild(a); + wordCloudList.appendChild(li); + } + }); + + if (displayedKeywordsCount > 0) { + wordCloudElement.classList.remove('hidden'); + } else { + wordCloudElement.classList.add('hidden'); + } + + if (displayedKeywordsCount < totalRelevantKeywords) { + console.warn(`Some relevant keywords (${totalRelevantKeywords - displayedKeywordsCount}) could not be displayed.`); + } +} \ No newline at end of file diff --git a/docs/javascripts/keywordsFinder.js b/docs/javascripts/keywordsFinder.js new file mode 100644 index 00000000..8a7c1a6b --- /dev/null +++ b/docs/javascripts/keywordsFinder.js @@ -0,0 +1,30 @@ +//delete links in markdown content +function cleanMarkdownContent(content) { + return content.replace(/\[[^\]]*\]\([^)]*\)/g, ''); +} + +function cleanWord(word) { + return word.replace(/[.,!?(){}[\]`-]/g, '').toUpperCase(); +} + +function findKeywordsInContent(currentPageMdPath, lexique, callback) { + fetchMarkdownContent(currentPageMdPath) + .then(content => { + const cleanContent = cleanMarkdownContent(content); + const wordCounts = {}; + + const words = cleanContent.split(/\s+/); + + words.forEach(word => { + const cleanedWord = cleanWord(word); + if (lexique.includes(cleanedWord)) { + wordCounts[cleanedWord] = (wordCounts[cleanedWord] || 0) + 1; + } + }); + + const filteredKeywords = Object.keys(wordCounts).filter(word => wordCounts[word] >= 2); + + callback(filteredKeywords); + }) + .catch(error => console.error('Error fetching markdown content:', error)); +} \ No newline at end of file diff --git a/docs/javascripts/levels.js b/docs/javascripts/levels.js new file mode 100644 index 00000000..87d2cff8 --- /dev/null +++ b/docs/javascripts/levels.js @@ -0,0 +1,173 @@ +let levelSwitch, switchLabel, currentPageMdPath; + +function initializeLevelManagement() { + levelSwitch = document.getElementById("level-switch"); + switchLabel = document.querySelector(".switch-label"); + const savedLevel = localStorage.getItem("userLevel") || "expert"; + + levelSwitch.checked = savedLevel === "expert"; + updateSwitchLabel(); + + addLevelTags(); + + filterContent(savedLevel); + updateTOCVisibility(savedLevel); + currentPageMdPath = getCurrentPageMdPath(); + levelSwitch.addEventListener("change", handleLevelChange); +} + + +function getCurrentPageMdPath() { + let currentPagePath = window.location.pathname; + if (currentPagePath.endsWith("/")) { + currentPagePath = currentPagePath.slice(0, -1); + } + return currentPagePath.replace(".html", ".md"); +} +function handleLevelChange() { + const selectedLevel = levelSwitch.checked ? "expert" : "beginner"; + localStorage.setItem("userLevel", selectedLevel); + updateSwitchLabel(); + filterContent(selectedLevel); + updateTOCVisibility(selectedLevel); + + let cachedKeywords = getCache("keywordsCache"); + let cachedDefinitions = getCache("definitionsCache"); + fetchKeywords(currentPageMdPath, cachedKeywords, cachedDefinitions); +} +//Display the level of the user +function updateSwitchLabel() { + switchLabel.textContent = levelSwitch.checked ? "Expert" : "Beginner"; +} + +function addLevelTags() { + const allContent = document.querySelectorAll("[data-level]"); + allContent.forEach((element) => { + const level = element.dataset.level; + if (level) { + const existingTag = element.querySelector(".level-tag"); + if (!existingTag) { + const tag = document.createElement("span"); + tag.className = `level-tag level-${level}`; + tag.textContent = level.charAt(0).toUpperCase() + level.slice(1); + element.insertBefore(tag, element.firstChild); + } + } + }); +} +function isHowtosSection() { + return window.location.pathname.includes('/Howtos/'); +} +function filterContent(level) { + const articleContent = document.querySelector('.article-content'); + if (!articleContent) return; + + const sections = articleContent.querySelectorAll('.collapse-section'); + + if (isHowtosSection()) { + const hasBeginnerSections = Array.from(sections).some(section => + section.querySelector('h2[data-level="beginner"]') + ); + + sections.forEach(section => { + const h2Element = section.querySelector('h2'); + const sectionLevel = h2Element ? h2Element.dataset.level : null; + const isAllSection = sectionLevel === 'all'; + const span = section.querySelector('.level-tag'); + + if (level === 'expert' || isAllSection) { + handleExpertOrAllSection(section, span, level, isAllSection); + } else if (level === 'beginner') { + if (hasBeginnerSections) { + handleBeginnerSectionWithTags(section, sectionLevel, isAllSection, span); + } else { + handleBeginnerSectionWithoutTags(section, sectionLevel, isAllSection, span); + } + } + }); + } else { + // For sections that are not in "Howtos" section + sections.forEach(section => { + section.classList.remove('hidden-level'); + const span = section.querySelector('.level-tag'); + if (span) span.style.display = 'none'; + }); + } +} + +function handleBeginnerSectionWithTags( + section, + sectionLevel, + isAllSection, + span +) { + if (sectionLevel === "beginner" || isAllSection) { + section.classList.remove("hidden-level"); + if (span) { + span.style.display = sectionLevel === "beginner" ? "" : "none"; + } + } else { + section.classList.add("hidden-level"); + } +} + +function handleBeginnerSectionWithoutTags( + section, + sectionLevel, + isAllSection, + span +) { + section.classList.remove("hidden-level"); + if (span) { + span.style.display = "none"; + } +} + +//Remove tag "expert" and "all" +function handleExpertOrAllSection(section, span, level, isAllSection) { + section.classList.remove("hidden-level"); + if (span && (level === "expert" || isAllSection)) { + span.style.display = "none"; + } +} +function updateTOCVisibility(level) { + if (!isHowtosSection()) { + // Hors section Howtos, tout afficher + document.querySelectorAll(".md-nav__item, .md-nav").forEach((item) => { + item.style.removeProperty('display'); + }); + return; + } + + const tocItems = document.querySelectorAll(".md-nav__list > .md-nav__item"); + + tocItems.forEach((item) => { + const link = item.querySelector(":scope > a.md-nav__link"); + if (link) { + const subNav = item.querySelector('.md-nav'); + const subItems = subNav ? subNav.querySelectorAll('.md-nav__item') : []; + + if (level === "expert") { + // En mode expert, tout afficher + item.style.removeProperty('display'); + subItems.forEach(subItem => subItem.style.removeProperty('display')); + } else { + // En mode beginner + item.style.removeProperty('display'); // Toujours afficher les éléments de premier niveau + + // Cacher les sous-éléments + subItems.forEach(subItem => subItem.style.display = 'none'); + } + } + }); +} +document.addEventListener("DOMContentLoaded", function () { + initializeLevelManagement(); + const savedLevel = localStorage.getItem("userLevel") || "expert"; + updateTOCVisibility(savedLevel); + + let cachedKeywords = getCache("keywordsCache"); + let cachedDefinitions = getCache("definitionsCache"); + + fetchKeywords(currentPageMdPath, cachedKeywords, cachedDefinitions); +}); diff --git a/docs/javascripts/main.js b/docs/javascripts/main.js new file mode 100644 index 00000000..0a097e1f --- /dev/null +++ b/docs/javascripts/main.js @@ -0,0 +1,24 @@ +document.addEventListener('DOMContentLoaded', function () { + let cachedKeywords = getCache('keywordsCache'); + let cachedDefinitions = getCache('definitionsCache'); + + let currentPagePath = window.location.pathname; + + if (currentPagePath.endsWith('/')) { + currentPagePath = currentPagePath.slice(0, -1); + } + + const currentPageMdPath = currentPagePath.replace('.html', '.md'); + + fetchKeywords(currentPageMdPath, cachedKeywords, cachedDefinitions); + +}); + +document.addEventListener('DOMContentLoaded', function() { + initializeLevelManagement(); + + let cachedKeywords = getCache('keywordsCache'); + let cachedDefinitions = getCache('definitionsCache'); + + fetchKeywords(currentPageMdPath, cachedKeywords, cachedDefinitions); +}); \ No newline at end of file diff --git a/docs/javascripts/modalFunctions.js b/docs/javascripts/modalFunctions.js new file mode 100644 index 00000000..d3751cc4 --- /dev/null +++ b/docs/javascripts/modalFunctions.js @@ -0,0 +1,55 @@ +function openModal(keyword, definition) { + + + const modal = document.getElementById("modal"); + const modalTitle = document.getElementById("modal-title"); + const modalDefinition = document.getElementById("modal-definition"); + // TODO: navigation to the keyword page is under development + + /* const modalLink = document.getElementById("modal-link"); */ + + if (modalTitle && modalDefinition /* && modalLink */) { + let descriptionText; + if (typeof definition === 'string') { + descriptionText = definition; + } else if (definition && typeof definition === 'object' && definition.description) { + descriptionText = definition.description; + } else { + descriptionText = 'Definition not available'; + } + /* const glossaryPageUrl = `${window.location.origin}/glossary/${keyword.toLowerCase()}/`; */ + + /* Removed the redirection logic */ + modalTitle.textContent = keyword; + modalDefinition.textContent = descriptionText; + /* modalLink.href = `${window.location.origin}/glossary/${keyword.toLowerCase()}/`; */ + modal.classList.remove("hidden"); + modal.style.display = "block"; + /* modalLink.classList.remove("hidden"); */ + modalLink.href = `${window.location.origin}/glossary/${keyword.toLowerCase()}/`; + modal.classList.remove("hidden"); + modal.style.display = "block"; + modalLink.classList.remove("hidden"); + + } else { + console.error('Modal elements not found'); + } +} + +document.addEventListener("DOMContentLoaded", function () { + document.getElementById("close-modal").addEventListener("click", function () { + const modal = document.getElementById("modal"); + modal.classList.add("hidden"); + modal.style.display = "none"; + }); + + document.getElementById("modal").addEventListener("click", function (event) { + if (event.target === event.currentTarget) { + const modal = document.getElementById("modal"); + modal.classList.add("hidden"); + modal.style.display = "none"; + } + }); + + window.openModal = openModal; +}); \ No newline at end of file diff --git a/docs/stylesheets/collapse_section.css b/docs/stylesheets/collapse_section.css new file mode 100644 index 00000000..45aef5e0 --- /dev/null +++ b/docs/stylesheets/collapse_section.css @@ -0,0 +1,103 @@ +.collapse-section { + margin-bottom: 1.5em; + border-radius: 8px; + overflow: hidden; + transition: box-shadow 0.1s ease; + box-shadow: 0 0 0 rgba(0, 0, 0, 0); +} + +.collapse-section:hover { + border-color: transparent; + +} + +.collapse-section h2 { + margin: 0; + padding: 1em; + background-color: #ebe8e8; + cursor: pointer; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 1.2em; + color: #333; + transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out; +} + +.collapse-section h2:hover { + background-color: #dddcdc; +} + +.collapse-section.active h2 { + color: #d94412; +} + +[data-md-color-scheme="slate"] .collapse-section { + background-color:#333; +} + + + +[data-md-color-scheme="slate"] .collapse-section h2 { + background-color: #333; + color: #e0e0e0; +} + +[data-md-color-scheme="slate"] .collapse-section h2:hover { + background-color: #3a3a3a; +} + +[data-md-color-scheme="slate"] .collapse-section.active h2 { + color: #ff6e42; +} + +[data-md-color-scheme="slate"] .collapse-section.active h2 { + color: #ff6e42; +} + +.collapse-icon { + transition: transform 0.3s ease; +} + +.collapse-section.active { + max-height: none; +} + +.collapse-section.active .collapse-icon { + transform: rotate(180deg); +} + +.collapse-content { + max-height: 0; + overflow: hidden; + transition: max-height 0.1s ease-in-out; + padding: 0 1em; +} + +.collapse-section.active .collapse-content { + max-height: 100%; + padding: 1em; +} + +[data-md-color-scheme="slate"] .collapse-content { + background-color: #1E2229; + color: #e0e0e0; +} + +.content-wrapper { + display: flex; + flex-direction: column; + min-height: 100vh; + overflow-y: auto; +} + +.article-content { + flex-grow: 1; +} + +.md-footer { + position: sticky; + bottom: 0; + width: 100%; + z-index: 10; +} \ No newline at end of file diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index b56f5af1..f8b6b669 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,56 +1,97 @@ #welcome { - text-align: center; - margin-bottom: 1em; -} - -#home-logo { + text-align: center; margin-bottom: 1em; -} - -[data-md-color-scheme="slate"] { - --md-hue: 217; - --md-typeset-a-color: #d94412 !important; -} - -[data-md-color-scheme="gpac"] { - /* header background */ - --md-primary-fg-color: #d7d7d7; - /* header text */ - --md-primary-bg-color: #141414; - - --md-primary-fg-color--light: hsla(0, 0, 95%, 1); - --md-primary-fg-color--dark: hsla(0, 0, 25%, 1); - - --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7); + } + + #home-logo { + margin-bottom: 1em; + } + + [data-md-color-scheme="slate"] { + --md-hue: 217; + --md-typeset-a-color: #d94412 !important; + --md-accent-keywords-color: hsla(0, 0%, 75%, 1) !important; + --md-icon-color: #a7a7a7; - --md-accent-fg-color: #d94412; - --md-accent-fg-color--transparent: #d944121f; - --md-accent-bg-color: #d94412; - --md-accent-bg-color--light: #d94412e0; - - --md-typeset-a-color: #d94412; + --md-glossary-bg-color: #2e2e2e; + --md-glossary-text-color: #e0e0e0; + --md-keyword-cloud-bg-color: #3a3a3a; + --md-keyword-cloud-text-color: #f0f0f0; + } + + [data-md-color-scheme="gpac"] { + /* header background */ + --md-primary-fg-color: #E0E0E0; + /* header text */ + --md-primary-bg-color: #141414; + + --md-primary-fg-color--light: hsla(0, 0%, 95%, 1); + --md-primary-fg-color--dark: hsla(0, 0%, 25%, 1); + + --md-primary-bg-color--light: #F0F0F0; + + --md-accent-fg-color: #d94412; + --md-accent-fg-color--transparent: #d944121f; + --md-accent-keywords-color: hsla(0, 0%, 25%, 1) !important; + --md-accent-bg-color: #d94412; + --md-accent-bg-color--light: #d94412e0; + + --md-typeset-a-color: #d94412; + --md-icon-color: #8e8686; + + /* default is too bright on poorly calibrated screens */ + --md-code-bg-color: #e0e0e0; + + --md-glossary-bg-color: #f5f5f5; + --md-glossary-text-color: #333333; + --md-keyword-cloud-bg-color: #ffffff; + --md-keyword-cloud-text-color: #000000; + + /* force tabs to match gpac.io styles */ + .md-tabs__item .md-tabs__link { + transition: unset !important; + background: none !important; + color: inherit !important; + opacity: 1 !important; + } + + .md-tabs__item:focus, + .md-tabs__item:hover { + background-color: #151515 !important; + color: #fff !important; + } + + .md-tabs__item--active { + color: var(--md-typeset-a-color); + font-style: italic; + } - /* default is too bright on pourly calibrated screens */ - --md-code-bg-color: #e0e0e0; - - /* - force tabs to match gpac.io styles - */ - .md-tabs__item .md-tabs__link { - transition: unset !important; - background: none !important; - color: inherit!important; - opacity: 1 !important; - } - .md-tabs__item:focus, - .md-tabs__item:hover { - background-color: #151515 !important; - color: #fff !important; - } - - .md-tabs__item--active { - color: var(--md-typeset-a-color); - font-style: italic; - } + .md-header__option { + background-color: var(--md-primary-fg-color) !important; + } + } + + .md-typeset .highlight button { + display: block !important; + } + + .md-footer { + flex-shrink: 0; + } + + + .glossary-container, + .words-cloud { + background-color: var(--md-glossary-bg-color); + color: var(--md-glossary-text-color); + } + + .words-cloud-container { + background-color: var(--md-keyword-cloud-bg-color); + color: var(--md-keyword-cloud-text-color); + } + -} + .words-cloud a { + color: var(--md-typeset-a-color); + } \ No newline at end of file diff --git a/docs/stylesheets/feedback.css b/docs/stylesheets/feedback.css new file mode 100644 index 00000000..68536b98 --- /dev/null +++ b/docs/stylesheets/feedback.css @@ -0,0 +1,4 @@ + +.md-feedback__icon.md-icon[data-md-value="1"]:hover svg path { + fill: #45a049; +} \ No newline at end of file diff --git a/docs/stylesheets/glossary-pages.css b/docs/stylesheets/glossary-pages.css new file mode 100644 index 00000000..558ab371 --- /dev/null +++ b/docs/stylesheets/glossary-pages.css @@ -0,0 +1,6 @@ + + +body.glossary-page h1 { + color: #d94412 !important; + font-weight: bold; +} \ No newline at end of file diff --git a/docs/stylesheets/keyword-cloud.css b/docs/stylesheets/keyword-cloud.css new file mode 100644 index 00000000..3a3fa2b9 --- /dev/null +++ b/docs/stylesheets/keyword-cloud.css @@ -0,0 +1,121 @@ +.words-cloud-container { + height: auto; + max-height: 60vh; + overflow-y: auto; + text-align: center; + margin-top: 1rem; + border-radius: 8px; + overflow: hidden; + + } + [data-md-color-scheme="slate"] .words-cloud-container { + background-color: #333; + } + + .words-cloud { + padding: 0.5rem; + background-color: #f4f4f4; + border: 1px solid #e0e0e0; + border-radius: 8px; + margin-right: 1rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + transition: background-color 0.3s ease-in-out; + border-radius: 8px; + margin-right: 1rem; + + + } + + [data-md-color-scheme="slate"] .words-cloud { + background-color: #333; + border-color:#333; + box-shadow: 0 1px 2px rgba(255, 255, 255, 0.05); + + } + .words-cloud-container:hover .words-cloud { + background-color: #dddcdc; + + } + + [data-md-color-scheme="slate"] .words-cloud-container:hover .words-cloud { + background-color: #333; + + } + + + .words-cloud__title { + font-size: 14px; + text-transform: uppercase; + text-align: center; + padding: 0.5rem; + margin-bottom: 0.5rem; + color: #333; + border-bottom: 1px solid #e0e0e0; + } + + [data-md-color-scheme="slate"] .words-cloud__title { + color: #e0e0e0; + border-bottom-color: #333; + } + + .words-container { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + } + + #dynamic-words-cloud { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + margin: 0; + padding: 0; + list-style: none; + } + + .words-cloud li { + display: inline-block; + padding: 0.25rem 0.5rem; + transition: transform 0.2s, color 0.2s; + } + + .words-cloud a { + text-decoration: none; + } + + .words-cloud .size-1 { font-size: 10px; } + .words-cloud .size-2 { font-size: 12px; } + .words-cloud .size-3 { font-size: 14px; } + .words-cloud .size-4 { font-size: 16px; } + .words-cloud .size-5 { font-size: 18px; } + + .words-cloud .color-1 { color: var(--md-accent-fg-color); } + .words-cloud .color-2 { color: #666; } + .words-cloud .color-3 { color: #333; } + .words-cloud .color-4 { color: #d94412; } + + [data-md-color-scheme="slate"] .words-cloud .color-1 { color: var(--md-accent-fg-color); } + [data-md-color-scheme="slate"] .words-cloud .color-2 { color: #bbb; } + [data-md-color-scheme="slate"] .words-cloud .color-3 { color: #ddd; } + [data-md-color-scheme="slate"] .words-cloud .color-4 { color: #ff6633; } + + .words-cloud li:hover { + transform: scale(1.1); + color: var(--md-accent-fg-color); + } + + .modal-link { + display: inline-block; + margin-top: 1rem; + font-size: 14px; + color: var(--md-accent-fg-color); + text-decoration: none; + border-bottom: 1px dotted var(--md-accent-fg-color); + } + + [data-md-color-scheme="slate"] .modal-link { + color: var(--md-accent-fg-color); + border-bottom-color: var(--md-accent-fg-color); + } \ No newline at end of file diff --git a/docs/stylesheets/levels.css b/docs/stylesheets/levels.css new file mode 100644 index 00000000..f0bc2eb1 --- /dev/null +++ b/docs/stylesheets/levels.css @@ -0,0 +1,84 @@ +.hidden-level { + display: none; + } + + .level-tag { + padding: 2px 6px; + border-radius: 4px; + font-size: 0.8em; + font-weight: bold; + margin-right: 5px; + } + + .level-beginner { background-color: #e6f3e6; color: #2e7d32; } + .level-expert { background-color: #ffebee; color: #c62828; } + + [data-md-color-scheme="slate"] .level-beginner { + font-weight: 600; + color: #86efac; + background-color: #14532d + } + + .switch-container { + display: flex; + align-items: center; + + + } + + .switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; + } + + .switch input { + opacity: 0; + width: 0; + height: 0; + } + + .slider { + position: absolute; + cursor: pointer; + top: 10px; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: .4s; + } + + .slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 0; + background-color: white; + transition: .4s; + } + + input:checked + .slider { + background-color: #d94412; + } + + input:checked + .slider:before { + transform: translateX(30px); + } + + .slider.round { + border-radius: 34px; + } + + .slider.round:before { + border-radius: 50%; + } + + .switch-label { + margin-top: 10px; + margin-left: 10px; + font-weight: bold; + } \ No newline at end of file diff --git a/docs/stylesheets/modal.css b/docs/stylesheets/modal.css new file mode 100644 index 00000000..3b59ed64 --- /dev/null +++ b/docs/stylesheets/modal.css @@ -0,0 +1,83 @@ +.modal { + z-index: 1000; + position: absolute; + display: none; + margin-top: 10px; + width: 100%; + max-width: 400px; + left: 50%; + transform: translateX(-50%); +} + +.modal-content { + background-color:#E0E0E0; + border: 1px solid #e0e0e0; + border-radius: 4px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + padding: 20px; + width: 100%; + transition: opacity 0.25s ease; +} + +.modal-header { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 16px; +} + +.modal-header i { + color: #d94412; + font-size: 20px; + margin-right: 8px; +} + +.modal-header h2 { + margin: 0; + font-size: 1 rem; + font-weight: bold; + color: #333; +} + +.modal-content__definition { + font-size: 14px; + line-height: 1.6; + color: #4a4a4a; + text-align: justify; + margin-bottom: 16px; +} + +.modal-close { + position: absolute; + top: 8px; + right: 8px; + background: none; + border: none; + font-size: 18px; + cursor: pointer; + color: #666; + transition: color 0.2s ease; +} + +.modal-close:hover { + color: #d94412; +} + +.modal-link { + display: inline-block; + padding: 6px 12px; + background-color: #d94412; + color: #ffffff; + text-decoration: none; + border-radius: 4px; + font-size: 14px; + transition: background-color 0.2s ease; +} + +.modal-link:hover { + background-color: #b23600; +} + +.hidden { + display: none !important; +} \ No newline at end of file diff --git a/docs/stylesheets/toggle.css b/docs/stylesheets/toggle.css new file mode 100644 index 00000000..0b786fdb --- /dev/null +++ b/docs/stylesheets/toggle.css @@ -0,0 +1,69 @@ + +@media screen and (max-width: 76.25em) { + .toggle-btn, + .toggle-btn i{ + display: none; + } +} + +@media screen and (min-width: 76.25em) { + .md-nav--secondary .md-nav__title .md-nav__icon { + display: block; + } +} +@media screen and (min-width: 76.25em) { + .md-nav__icon { + transition: background-color .25s; + width: auto; + } +} + +@media screen and (min-width: 76.25em) { + .md-nav__icon:hover { + background-color: transparent; + } +} + + +.toggle-btn { + margin-bottom: 0.2rem; + background-color: transparent; + color: white; + border: none; + border-radius: 50%; + cursor: pointer; + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + width: 50%; + z-index: 99999; + transition: background-color 0.3s ease, transform 0.3s ease; +} + +.toggle-btn i:hover { + background-color: #D7D7D7; +} + +.toggle-btn i { + color: #d94412e0; + transition: transform 0.3s ease; +} + +.hidden { + opacity: 0; + visibility: hidden; + transition: opacity 0.5s ease, visibility 0.5s ease; +} + +.visible { + opacity: 1; + visibility: visible; + transition: opacity 0.5s ease, visibility 0.5s ease; +} + + + +.span-toggle { + margin-right: 4px; +} \ No newline at end of file diff --git a/docs/stylesheets/top.css b/docs/stylesheets/top.css new file mode 100644 index 00000000..e3c02d4f --- /dev/null +++ b/docs/stylesheets/top.css @@ -0,0 +1,32 @@ +.md-top.md-icon { + margin-left: 75% !important; + top: 85% !important; + background-color: #fff !important; + color: #8e8686; + transition: background-color 0.3s ease, color 0.3s ease, border 0.3s ease; + } + + .md-top:focus, .md-top:hover { + background-color: #d7d7d7 !important; + color: black !important; + border: 1px solid var(--md-default-fg-color--light) !important; + transition-duration: 300ms !important; + } + + .md-top[hidden] { + transition-duration: 300ms !important; + } + + + [data-md-color-scheme="slate"] .md-top.md-icon { + background-color: #2e2e2e !important; + color: #a7a7a7; + border: 1px solid #444; + } + + [data-md-color-scheme="slate"] .md-top:focus, + [data-md-color-scheme="slate"] .md-top:hover { + background-color: #3a3a3a !important; + color: #ffffff !important; + border: 1px solid #ff6633 !important; + } \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 440a1754..3e94af79 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,10 +1,34 @@ site_name: GPAC wiki site_url: https://wiki.gpac.io/ site_author: GPAC contributors +extra: + keywords_file: 'data/keywords.json' +extra_javascript: + + - javascripts/main.js + - javascripts/levels.js + - javascripts/fetchFunctions.js + - javascripts/domManipulation.js + - javascripts/keywordsFinder.js + - javascripts/keywordsDisplay.js + - javascripts/cache.js + - javascripts/modalFunctions.js + + extra_css: - stylesheets/extra.css + - stylesheets/keyword-cloud.css + - stylesheets/top.css + - stylesheets/toggle.css + - stylesheets/modal.css + - stylesheets/glossary-pages.css + - stylesheets/collapse_section.css + - stylesheets/feedback.css + - stylesheets/levels.css + - https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css theme: name: material + custom_dir: overrides logo: images/gpac-logo.svg favicon: images/favicon-32x32.png palette: @@ -25,8 +49,10 @@ theme: icon: material/brightness-4 name: Switch to light mode features: + - navigation.top - tables - def_list + - content.code.select - content.code.annotate - content.code.copy - content.tooltips @@ -39,6 +65,7 @@ theme: - search.highlight - search.share - search.suggest + - search repo_url: https://github.com/gpac/gpac/ plugins: - search @@ -46,14 +73,17 @@ plugins: hooks: - scripts/mkdocs_hooks.py markdown_extensions: - - admonition - - attr_list - - toc: +- admonition +- attr_list +- toc: permalink: true - - nl2br - - sane_lists - - pymdownx.details - - pymdownx.superfences +- nl2br +- sane_lists +- md_in_html +- pymdownx.details +- pymdownx.superfences +- pymdownx.inlinehilite + extra: analytics: provider: google @@ -356,3 +386,7 @@ nav: - TTXT Format: xmlformats/TTXT-Format-Documentation.md - NHML format: xmlformats/NHML-Format.md - NHNT format: xmlformats/NHNT-Format.md + # TODO: implement keyword pages + # - Glossary: + # - glossary/encode.md + # - glossary/encrypt.md \ No newline at end of file diff --git a/overrides/base.html b/overrides/base.html new file mode 100644 index 00000000..1541680e --- /dev/null +++ b/overrides/base.html @@ -0,0 +1,276 @@ +{#- + This file was automatically generated - do not edit + -#} + {% import "partials/language.html" as lang with context %} + + +
+ {% block site_meta %} + + + {% if page.meta and page.meta.description %} + + {% elif config.site_description %} + + {% endif %} + {% if page.meta and page.meta.author %} + + {% elif config.site_author %} + + {% endif %} + {% if page.canonical_url %} + + {% endif %} + {% if page.previous_page %} + + {% endif %} + {% if page.next_page %} + + {% endif %} + {% if "rss" in config.plugins %} + + + {% endif %} + + + {% endblock %} + {% block htmltitle %} + {% if page.meta and page.meta.title %} +SZjWvp_uSpyn)~nbvYHttk0z;o=k1&Ldi&HHlq6a*o^3tq;$hoVgD${4!wR!b=OP
zFm>W&m%M(ls(-2De$3th4$eKKR$)zk4B915VyWISDtZ-{ON(CkhYAG1)_|R;)f9OB
zl)MaO!zB^vYJg=c(rM&{Aq5`TKV9TJ^)y~Uje4AZi3 uu_f@~#0?5O95mr&CLO`AK*
z8)^3Rx!WoD)R|X@Q`9Kmznt-R8OHN+b$Yf*R{e_UFc#^)?>(urc3h@U<=2Zf{oH^X
z_>b@hgDAJ O#Y}6SRH`IE4BDlK}%9!z5JT~azjW^$LKeLZc%1$*rLVfRO^!!#zNCb@5Se`
z_4;)vsnb5MbK1#7Xp)4_2f~k!fgwz a syYa?(+8k%#gthfZ8+htsZ-au1bsTkV_PIG1afHhjGCHSM%~
z;vs)cQr?eUoYa|j7yB?Md#!#C0-M#s@1@y%_wh`^Z!AY!i|m#-VqTtr@{uwpLXDt>
z!n#H5Lf_Nd1=5VgwtYsys)5hWyRI(BkzI=uF(f7!KUu~wWQm%4AdaSCGr_R=4m`3f
zM9S`l_aYZyUqe~Wp_np8eR2362!YiiZiHTb(G7K}ir$}md#OW9AN||fp1}T 1T*E86Tav86K@fSR;44RTDP%V9m{`>m0^-LS#+2J<>k`|_mT^7v`vS_0%!
zcdbpGesLrLnF^xbTn|NF&5`Vc=~K?lOthYHFWCQy^XM6vsuAFPJ^V(#L6v!ljV^6h
zy?Gbd02xFD{xo5scg$oZlH2S-*+0FEF%7NVMAq7>pto9t3MXumn&|=Y{hlz~T4b4Z
z!4F8}Cy;?c8qrD-(frHnG5eervWXu2g=FE`N_aEf67;7RX)B~~@?3338r2d#X8~UA
z=ZI^?hRawDyZAOG6*%{hk9*fNhSQvcZqiGhHV`TmEJ-(I&QjG@nZags#TAAxE`|7V
z^G$ub5pxiG3u|ts7LhL)N1}_OpU$Wc{XMA}d9u2MvT9eH<=o+#&aK!NC0r4b_
z_1|CPHwNtViX+~YaBn`XZw2ocU6u33+}^ac_uoEV+HfRJ5}u7k-u*zLpY{cvKqnEO
z9+dlu_n13$R4BY|z1nz=)$sV$@D#{@{#Xbn&=E|!=iRQ5FDn2IP9>Ym@wnQvAMo(~
zDC`b&avj8x>6!m*ICGfxZOzQs6|}Ys5vI+YJ~kX9LcgWBbIPGj;icVnsH|EV>K-$p
zLf?2-!~)7J
Of@~8{gnz;-<}!UN7ESlf4M;<3&pvWs--?Yt>MaDen-CBG=G?H#dVJY
zfQ_4i{0|PbelzHjG9-<7O@z4WgC!>k%@-GX%5M>j_Ex^wg1E1G>B%|?!uZF-cayH}
zy%9oYRSTw*NfA#yBi^_(nr=bl3&BMEYTm%UrtimAj#st{S#Ln;FCK4M$I0^;aYYNV
zO><*0%iPgt3+|_Oc9loc7NQll(n}tOFxI?if6xZ(IZ|l_NPh{PXS}@1Wls-p3ILSM
zUE}hGyeWcQJu@WapC_~l`jDF~jc(
rIJl?4LTc3LM(1Dk
z&d<2wptVAVOvJf<7@t%@1CoPkbr5(ik*kS4_iP7ie@=FDJAnJF);jv=e&AW@G=)%}
z#c5+mqC*za`7P1ug_F88^E(CX{_=sAe^_(22D(&}S0Zd$(l1+!na<0f?@6D_^4r5H
zGsY??GT?5u0%-~^Tok3`B1n#R7}Tp4x
nKoL>QmlsAT^TjHWz8pgKTF1d1|uSBYxdYX5WLdGH-_fUn--n
zhh0?i7Ovm|xmc;%X~3%5aXgldpMYCm_KS