Resolving a Memory Leak in Shaka Player: Custom EMSG Removal for Streaming Performance

In streaming media applications for smart TVs (LG and Samsung), maintaining consistent performance during long playback sessions is essential. While developing our streaming apps using Shaka Player v4.16.0, we encountered escalating JavaScript heap usage that eventually led to crashes during extended playback. The culprit? Inefficient EMSG (Event Message Boxes) handling for ad signaling in DASH streams. This post explains the problem, how we diagnosed it, and our targeted fix applied across multiple TV apps.

Identifying the Issue: Heap Growth in Long Sessions

Our apps are designed for continuous playback of live and on-demand content. During testing on mid-range smart TVs, playback was smooth initially. However, after roughly 90 minutes of continuous streaming, problems appeared:

  1. Increased buffering
  2. UI responsiveness degradation
  3. Out-of-memory crashes

Profiling in Chrome DevTools revealed a dramatic surge in heap usage—from 40-50 MB at startup to over 450 MB. Heap snapshots showed Uint8Array objects accumulating from media segment parsing, which became critical on resource-constrained TV hardware.

Understanding EMSG Boxes: Inband Event Handling

Shaka Player is widely used for DASH and HLS playback in web-based TV apps. One feature of DASH is EMSG boxes, which carry inband metadata for events such as ad signaling, SCTE-35 markers, or accessibility cues. Shaka routes these events internally, usually into a timeline for synchronization with media playback.

In compiled Shaka v4.16.0, the function handling EMSG events appeared as:

        
       Ck: function (f) {
          a.ib &&
            am(a.ib, {
              schemeIdUri: f.schemeIdUri,
              startTime: f.startTime,
              endTime: f.endTime,
              id: String(f.id),
              emsg: f,
            });
        }

        
        

This function stores each EMSG event in memory through the call am(a.ib, { ... }), which adds it to Shaka’s region timeline (a.ib). Since Shaka retains full segment buffers, memory can grow over time—especially in streams with frequent ad markers.

In the uncompiled source, this logic appears in lib/player.js around line 2640, where EMSG events are parsed and added to shaka.media.RegionTimeline via addRegion().

Shaka includes pruning logic in lib/media/region_timeline.js within the prune(startTime) method, which removes regions whose endTime <= startTime. However, this may not always clear all old entries if events have long durations or the live window moves slowly.

To mitigate memory buildup, developers can monitor the timeline size or manually prune old regions when needed.

Root Cause: Unpruned Buffer Copies

The main issue was that Shaka stored full segment buffers from EMSG events indefinitely. In streams with frequent markers, thousands of these buffers accumulated, causing heap growth and performance degradation. On TV devices with limited RAM and slower garbage collection, this problem became particularly severe.


Resolution: Workaround EMSG Handling

To address the memory accumulation, we worked directly with the compiled Shaka v4.16.0 build. We downloaded the compiled code and commented out the contents of the EMSG function , preventing unnecessary memory accumulation:

                        
                            Ck: function (f) {
                              // Disabled to prevent memory growth
                              // a.ib && am(a.ib, {
                              //   schemeIdUri: f.schemeIdUri,
                              //   startTime: f.startTime,
                              //   endTime: f.endTime,
                              //   id: String(f.id),
                              //   emsg: f,
                              // });
                            }

                        
                    


💡 Important notes:

  1. There’s no configuration option in Shaka to disable EMSG; it must be handled either by forking the source or modifying the compiled build as we did.
  2. Function names differ between Shaka versions, so solutions are version-specific.
  3. We shifted ad signaling to manifest-based methods to maintain functionality without EMSG

This lightweight change solved the memory accumulation problem and improved long-session performance across all our TV apps.

Got an idea? We can take it further.

Scroll