‘동영상이 재생되지 않음’ 이 네 가지 다른 버그로 밝혀졌습니다.

작성자

카테고리:

← 피드로
DEV Community · Sai Keerthana P · 2026-09-05 개발(SW)

Our app ships long video lessons. Twenty to forty minutes each, watched mostly on hostel wifi and mobile data with two bars.

Streaming video looks like a solved problem when you build the happy path. You wire up ExoPlayer, point it at a stream, it plays. Then real students open it on devices you have never held, and your bug tracker starts filling up with one line: “video not playing.”

That line turned out to be four unrelated problems. Here’s what I actually learned.

## Buffering is the bug you feel before you see an error

Nobody waits out a bad connection during a 35-minute lecture. On a 15-second reel a stall is annoying. In a lecture, a stall every two minutes means the student closes the app and studies from a PDF instead.

So the real question was never “does it play.” It was “what happens when the network stops cooperating for four seconds.”

Three situations broke us repeatedly:

  • Wifi to mobile data handover mid-lesson
  • Bandwidth that is technically connected but useless
  • Short total drops, three to ten seconds, that should not end playback

ExoPlayer gives you everything you need here. Adaptive bitrate, a configurable LoadControl, player listeners. The catch is that the defaults are tuned for general media, not for a 40-minute lecture on a bad link. What moved the numbers for us:

Bigger buffers, deliberately. DefaultLoadControl.Builder().setBufferDurationsMs(...) takes a min buffer, a max buffer, the buffer needed to start playback, and the buffer needed to resume after a rebuffer. That last parameter is the interesting one. Raising it costs you a little extra time on resume and buys you far fewer repeat stalls, because the player stops trying to restart on a nearly empty buffer.

Telling the user the truth. Our first version showed a spinner and nothing else. A spinner with no context reads as “the app is frozen,” so students force-closed and reopened, which threw away the buffer and made everything worse. Saying “reconnecting” instead of spinning silently cut that behaviour noticeably.

Retrying instead of dying. A dropped connection throws a playback error, and by default that is the end of the session. Retrying recoverable errors with backoff turned a three-second blip into a pause and a resume.

If I had to compress this part into one sentence: for education video, perceived reliability beats picture quality. Students will happily watch a softer 480p that never stops over a crisp 1080p that stutters every two minutes. I would not have believed that before watching the retention numbers.

Then the weird ones start

Once flaky-but-working networks were handled, we were left with failures that all look identical from the outside and share nothing underneath. What finally made these tractable was boring: stop guessing from the symptom, read the error code and the cause string literally, and ask what has to be true for that exact error to fire.

DRM errors. A “key not available” style failure fires during decryption, after a licence has already been fetched. That is a different thing from a licence that expired or never loaded, and the distinction tells you where to look. Ours pointed at a server-side token and content-packaging mismatch, not at client code. The tell was that it reproduced identically on unrelated devices. Client bugs are rarely that polite.

Network errors dressed as DRM errors. A TLS handshake failure happens before any decoding starts. Ours showed up inside a DRM cause field, which sent me down a licensing rabbit hole for longer than I want to admit. It was the transport failure being reported through the DRM data path. Mostly transient. The useful move is checking whether it clusters on one network or region, not “fixing” it.

Decoder resource errors. The OS reclaimed the secure codec mid-playback. Secure decoders are a scarce system resource, and the platform can take one away when you background the app or when memory gets tight. There is no bug in your player logic to find here. The only lever you have is best-effort recovery: catch it, re-initialize the player, seek back to the last known position.

Format and capability errors. The device’s decoder is refusing the content outright, usually because the source file is non-standard. Wrong resolution or frame rate for the delivery profile. This is a content-pipeline fix, not a code fix. Do fail loudly though, with a message a support person can act on, instead of silently showing a black rectangle.

What I’d tell myself a year ago

Four bugs, one symptom, four different owners: backend, network infra, platform limits, content pipeline. Not one of them was fixed by editing the same file.

In video playback, the error code is usually more informative than the stack trace wrapped around it. And the fix that matters most is almost never the interesting one. Ours was a buffer parameter and a string that said “reconnecting.”

If you’re debugging something similar, I’d genuinely like to hear which of the four bit you. Mine was the TLS failure hiding in a DRM field.

원문에서 계속 ↗