"libmpv 의 render/client API 로 AppKit NSView 안에 렌더링해."
영상을 보여주는 두 방식
embed 한 엔진은 어딘가엔 그려야 해. 게으른 방식은 엔진이 자기 창 열고 몰게 두는 거야. 시작은 빠른데 막다른 길이지. 그 창은 네 앱의 일부가 아니거든. 네 단축키가 거기 안 닿고, 전체화면 전환이 그거랑 싸우고, 온-비디오 오버레이는 얹힐 데가 없어. 결국 남의 TV 에 테이프로 붙인 리모컨 꼴이 돼.
맞는 방식은 libmpv 가 원래 그러라고 설계된 방식이야. render API 가 엔진을 네가 주는 표면에 그리게 해줘. Ashen Reel 은 AppKit NSView 를 내주고, 영상은 앱 자기 창 안에 그려져. responder chain 안에, 오버레이 아래에, 네가 통제하는 composition 의 일부로.
왜 '네 창 안' 이 모든 걸 바꾸나
영상이 네가 쥔 NSView 안에 사는 순간, 어려운 기능 한 무더기가 거의 공짜가 돼. seek 오버레이는 형제 뷰라서 위에 얹혀. 전체화면이랑 Spaces 는 그냥 평범한 창이라 잘 돌아가. 키보드 chord 는 responder chain 안이라 닿고. 더블클릭 전체화면이랑 스크롤 볼륨은 뷰가 이벤트를 받으니까 되고. 엔진이 자기 떨어진 창을 쥐고 있으면 그중 아무것도 안 돼.
render API 는 콜백으로 돌아. libmpv 가 자기 스레드에서 '새 프레임 준비됐어' 하고 신호하면, 넌 메인 스레드로 hop 해서 뷰 다시 그리라고 표시하고, AppKit 이 present 하라고 부르지. 단순하게 그리면 이런 모양이야.
보상은 네이티브처럼 '느껴지는' 게 아니라 네이티브 '인' 플레이어야. 빌린 엔진이 Ashen Reel 자기 유리에 픽셀을 칠하고, 모든 창이랑 키랑 오버레이가 진짜 그런 AppKit 앱답게 돌아가거든.