Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/container-codecs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Container (H.264/H.265)

# The image variant with a CEF build that plays H.264 and H.265 (docs/proprietary-codecs.md).
# A tag codecs-<version> (for example codecs-1.3.3) builds that commit and pushes
# <version>-codecs. Normal releases (v*.*.*) are not touched.
on:
push:
tags: ["codecs-*"]

concurrency:
group: container-codecs-${{ github.ref }}
cancel-in-progress: false

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- id: cef
name: Read the CEF build and refuse an existing tag
run: |
. docker/cef-codecs/cef.env
echo "url=${CEF_CODECS_URL}" >> "$GITHUB_OUTPUT"
echo "sha1=${CEF_CODECS_SHA1}" >> "$GITHUB_OUTPUT"
tag="${GITHUB_REF_NAME#codecs-}-codecs"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
if docker manifest inspect "${REGISTRY}/${IMAGE_NAME,,}:${tag}" >/dev/null 2>&1; then
echo "image tag ${tag} exists; tags are never moved" >&2
exit 1
fi

- uses: docker/setup-buildx-action@v3

- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ steps.cef.outputs.tag }}
type=raw,value=codecs
flavor: |
latest=false
labels: |
org.opencontainers.image.title=mxl-browser-source (H.264/H.265)
org.opencontainers.image.description=MXL media function: a web page rendered offscreen with CEF, built with proprietary codecs (H.264, H.265). Codec licensing is the user's responsibility.
org.opencontainers.image.source=https://github.com/LeeO86/mxl-browser-source
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.licenses=MIT
io.dmf.mxl.revision=218ddaa0a08c12ffe75fc475ae65aa3d9eef16d7

- uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
push: true
build-args: |
CEF_URL=${{ steps.cef.outputs.url }}
CEF_SHA1=${{ steps.cef.outputs.sha1 }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=codecs
cache-to: type=gha,mode=max,scope=codecs
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Image variant 1.3.3-codecs

- **H.264 and H.265 (proprietary codecs).** A second image, `<version>-codecs`, uses a CEF build of the same version with H.264 (`<video>`, MSE, WebRTC receive) and H.265 (`<video>`, MSE) decoding in software; the normal image cannot play them, so for example the WebRTC previews of other functions stayed black. The function itself is unchanged. **Licensing is the user's responsibility** ([`docs/proprietary-codecs.md`](docs/proprietary-codecs.md)).
- `docker/Dockerfile` takes `CEF_URL` (default: the CEF project's build as before); `docker/cef-codecs/` holds the patches and the URL and SHA-1 of the build; a tag `codecs-<version>` runs `.github/workflows/container-codecs.yaml`.
- Tests: `tests/codecs/codecs.html` (all OK in the variant: `<video>` and MSE for both codecs, WebRTC H.264 loopback; WebRTC H.265 is not offered).

## 1.3.3

- **Counters at 0 from the start** (SPEC §12, #14). Every event counter was created on its first event, so `page_hangs_total`, `preview_frames_dropped_total`, `renderer_crashes_total` and the like had no series (dashboards: "no data") until something happened. They are now exported at 0 when the function starts, with every label value the code can produce: `page_hangs_total`, `popups_blocked_total`, `downloads_blocked_total`, `preview_frames_total`, `preview_frames_dropped_total`, `page_loads_total{result}`, `renderer_crashes_total{reason}` (`abnormal`, `killed`, `crashed`, `oom`, `other`, `hung`), `js_dialogs_total{type}`, `permission_denied_total{type}`, `navigation_blocked_total{reason}`, `console_messages_total{level}`, `ograf_actions_total{action,result}`, `ograf_uploads_total{result}`, `ograf_restores_total{result}`, `interaction_events_total{type,result}` and, with audio, `audio_stream_events_total{event}`. Names, labels and meaning are unchanged.
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ Image size: about 900 MB uncompressed (CEF's `libcef.so` stripped of its debug s

CMake ≥ 3.24 and Ninja. Without CEF and libmxl the core library and its unit tests build anywhere (`cmake -S . -B build -G Ninja && cmake --build build && build/unit-tests`; needs `libsamplerate0-dev`). The process itself builds in `docker/Dockerfile` (CEF minimal distribution, MXL via vcpkg, nmos-cpp). Behind a TLS-intercepting proxy pass `EXTRA_CA_CERT_B64` and the proxy build args.

### Image with H.264 and H.265

`ghcr.io/leeo86/mxl-browser-source:<version>-codecs` is the same function with a CEF build that also plays H.264 (`<video>`, MSE, WebRTC receive) and H.265 (`<video>`, MSE), for example the WebRTC previews of other functions, which stay black in the normal image. **Licensing warning:** H.264 and H.265 are covered by patents; whoever deploys this variant is responsible for any licences that apply. How it is built: [`docs/proprietary-codecs.md`](docs/proprietary-codecs.md).

## License

MIT, see [`LICENSE`](LICENSE). The image contains CEF (BSD-3-Clause), Chromium and other components with their own licences: [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md), in the image under `/usr/share/doc/mxl-browser-source/` (CEF's `LICENSE.txt` and Chromium's `CREDITS.html` in `cef/`).
4 changes: 3 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ RUN npm run build
FROM ${UBUNTU} AS cef
ARG CEF_VERSION=144.0.21+g4f5b28c+chromium-144.0.7559.248
ARG CEF_SHA1=e6233049cec12ba108b4b920b499e2a082ffd0ae
# Another CEF build of the same version, e.g. the one with H.264/H.265 (docs/proprietary-codecs.md).
ARG CEF_URL=""
ARG EXTRA_CA_CERT_B64=""
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates curl bzip2 build-essential cmake ninja-build python3 \
Expand All @@ -29,7 +31,7 @@ RUN if [ -n "$EXTRA_CA_CERT_B64" ]; then echo "$EXTRA_CA_CERT_B64" | base64 -d >
COPY docker/fetch.sh /usr/local/bin/fetch.sh
WORKDIR /opt
# libcef.so ships with debug symbols (1.5 GB): stripped, the dynamic symbols stay for linking.
RUN url="https://cef-builds.spotifycdn.com/cef_binary_$(echo "$CEF_VERSION" | sed 's/+/%2B/g')_linux64_minimal.tar.bz2" \
RUN url="${CEF_URL:-https://cef-builds.spotifycdn.com/cef_binary_$(echo "$CEF_VERSION" | sed 's/+/%2B/g')_linux64_minimal.tar.bz2}" \
&& sh /usr/local/bin/fetch.sh "$url" cef.tar.bz2 "$CEF_SHA1" \
&& mkdir cef && tar xjf cef.tar.bz2 -C cef --strip-components=1 && rm cef.tar.bz2 \
&& strip --strip-unneeded cef/Release/libcef.so \
Expand Down
4 changes: 4 additions & 0 deletions docker/cef-codecs/cef.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# CEF build with H.264/H.265 for the image variant <version>-codecs (docs/proprietary-codecs.md).
# Same CEF version as docker/Dockerfile (CEF_VERSION); attached to the GitHub release cef-144.0.21-codecs.
CEF_CODECS_URL=https://github.com/LeeO86/mxl-browser-source/releases/download/cef-144.0.21-codecs/cef_binary_144.0.21-g4f5b28c-chromium-144.0.7559.248_linux64_minimal_codecs_r2.tar.bz2
CEF_CODECS_SHA1=0bd9c3db41c2465586cef9c579d1497f7d8763f8
121 changes: 121 additions & 0 deletions docker/cef-codecs/chromium-media-hevc.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
diff --git a/media/base/supported_types.cc b/media/base/supported_types.cc
index 117bb7eb6d..3b49f40263 100644
--- a/media/base/supported_types.cc
+++ b/media/base/supported_types.cc
@@ -232,6 +232,10 @@ bool IsDecoderHevcProfileSupported(const VideoType& type) {
if (!IsDecoderColorSpaceSupported(type.color_space)) {
return false;
}
+#if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS) && BUILDFLAG(USE_PROPRIETARY_CODECS)
+ // Software HEVC decoding through FFmpeg (this build).
+ return true;
+#else

#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
#if BUILDFLAG(PLATFORM_HAS_OPTIONAL_HEVC_DECODE_SUPPORT)
@@ -248,6 +252,7 @@ bool IsDecoderHevcProfileSupported(const VideoType& type) {
#else
return false;
#endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
+#endif // ENABLE_FFMPEG_VIDEO_DECODERS && USE_PROPRIETARY_CODECS
}

bool IsDecoderVp9ProfileSupported(const VideoType& type) {
@@ -579,7 +584,7 @@ bool IsEncoderOptionalVideoType(const media::VideoType& type) {

bool IsDecoderBuiltInVideoCodec(VideoCodec codec) {
#if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS) && BUILDFLAG(USE_PROPRIETARY_CODECS)
- if (codec == VideoCodec::kH264) {
+ if (codec == VideoCodec::kH264 || codec == VideoCodec::kHEVC) {
return true;
}
#endif // BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS) &&
diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc
index eba1a21b3b..a4be993fc7 100644
--- a/media/ffmpeg/ffmpeg_common.cc
+++ b/media/ffmpeg/ffmpeg_common.cc
@@ -64,7 +64,7 @@ VideoColorSpace GetGuessedColorSpace(const VideoColorSpace& color_space) {
const char* GetAllowedVideoDecoders() {
// This should match the configured lists in //third_party/ffmpeg.
#if BUILDFLAG(USE_PROPRIETARY_CODECS) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
- return "h264";
+ return "h264,hevc";
#else
return "";
#endif
diff --git a/media/ffmpeg/scripts/build_ffmpeg.py b/media/ffmpeg/scripts/build_ffmpeg.py
index 9ef6ba9afc..591c82c324 100755
--- a/media/ffmpeg/scripts/build_ffmpeg.py
+++ b/media/ffmpeg/scripts/build_ffmpeg.py
@@ -1001,9 +1001,9 @@ def ConfigureAndBuild(target_arch, target_os, host_os, host_arch,

# Google Chrome specific configuration.
configure_flags['Chrome'].extend([
- '--enable-decoder=aac,h264',
+ '--enable-decoder=aac,h264,hevc',
'--enable-demuxer=aac',
- '--enable-parser=aac,h264',
+ '--enable-parser=aac,h264,hevc',
])

configure_flags['ChromeAndroid'].extend([
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index fef13f6b8a..4d8e2c03e4 100644
--- a/media/filters/ffmpeg_video_decoder.cc
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -73,7 +73,6 @@ static int GetFFmpegVideoDecoderThreadCount(const VideoDecoderConfig& config) {
case VideoCodec::kUnknown:
case VideoCodec::kVC1:
case VideoCodec::kMPEG2:
- case VideoCodec::kHEVC:
case VideoCodec::kVP9:
case VideoCodec::kAV1:
case VideoCodec::kDolbyVision:
@@ -84,6 +83,7 @@ static int GetFFmpegVideoDecoderThreadCount(const VideoDecoderConfig& config) {
NOTREACHED();

case VideoCodec::kH264:
+ case VideoCodec::kHEVC: // Software HEVC (this build): same as H.264.
// Normalize to three threads for 1080p content, then scale linearly
// with number of pixels.
// Examples:
@@ -117,8 +117,9 @@ static void ReleaseVideoBufferImpl(void* opaque, uint8_t* data) {

// static
bool FFmpegVideoDecoder::IsCodecSupported(VideoCodec codec) {
- // We only build support for H.264.
- return codec == VideoCodec::kH264 && IsDecoderBuiltInVideoCodec(codec);
+ // H.264 and (in this build) HEVC.
+ return (codec == VideoCodec::kH264 || codec == VideoCodec::kHEVC) &&
+ IsDecoderBuiltInVideoCodec(codec);
}

FFmpegVideoDecoder::FFmpegVideoDecoder(MediaLog* media_log)
diff --git a/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc b/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc
index 5e025cfae4..4fe448a1be 100644
--- a/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc
+++ b/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc
@@ -194,6 +194,23 @@ bool FrameResources::Initialize(VideoPixelFormat format,

gfx::BufferUsage buffer_usage = gfx::BufferUsage::SCANOUT_CPU_READ_WRITE;

+#if BUILDFLAG(IS_WIN)
+ // For CEF OSR feature, currently there's no other place in chromium use RGBA.
+ // If the format is RGBA, currently CEF do not write to the texture anymore
+ // once the GMB is returned from CopyRequest. So there will be no race
+ // condition on that texture. We can request a GMB without a keyed mutex to
+ // accelerate and probably prevent some driver deadlock.
+ if (format == PIXEL_FORMAT_ARGB || format == PIXEL_FORMAT_ABGR) {
+ // This value is 'borrowed', SCANOUT_VEA_CPU_READ is probably invalid
+ // cause there's no real SCANOUT on Windows. We simply use this enum as a
+ // flag to disable mutex in the GMBFactoryDXGI because this enum is also
+ // used above in macOS and CrOS for similar usage (claim no other one will
+ // concurrently use the resource).
+ // https://chromium-review.googlesource.com/c/chromium/src/+/5302103
+ buffer_usage = gfx::BufferUsage::SCANOUT_VEA_CPU_READ;
+ }
+#endif
+
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
buffer_usage = gfx::BufferUsage::SCANOUT_VEA_CPU_READ;
#elif BUILDFLAG(IS_LINUX)
Loading
Loading