Search
🎏

Kollus 안드로이드 라이브 인코더 소개

URL
생성 일시
2025/09/05 14:16
최종 편집 일시
2025/09/06 05:20
태그
카테노이드
Android
Kollus
파일과 미디어
KOLLUS 라이브 서비스를 위한 안드로이드용 라이브 인코더 앱에 대해 소개 합니다. 안정적인 라이브 스트리밍을 구현하기 위해서는 카메라, 인코딩, 전송까지 긴밀히 연결된 기술 요소들이 필요합니다. 이번 글에서는 Kollus 라이브 서비스를 위한 안드로이드용 라이브 인코더 앱 [https://play.google.com/store/apps/details?id=com.kollus.live&hl=en]의 구조와 동작 방식을 살펴봅니다. RTMP 송출 과정부터 카메라 필터, OpenGL 기반의 렌더링 처리까지, 실제 구현에 참고할 수 있는 기술적 세부 사항을 정리하였습니다. KOLLUS 라이브 인코더 소개 안드로이드용 Kollus 라이브 인코더는 Kollus 고객이 라이브 콘솔을 통해 발급받은 정보를 통해 인증한 뒤, 제공된 RTMP 송출 주소를 통해 데이터를 전송합니다. 자세한 설명은 Kollus 라이브 QUICK GUIDE [https://catenoid-support.atlassian.net/wiki/spaces/SUP/pages/3311547/QUICK+GUIDE]를 참조해 주세요. 앱은 UI를 담당하고, Kollus 라이브 인코더 SDK (별도 제공)는 카메라 제어, 인코딩, 멀티플렉싱, RTMP 송출을 처리하는 구조로 동작합니다. (RTMP (Real-Time Messaging Protocol) [https://ko.wikipedia.org/wiki/%EB%A6%AC%EC%96%BC_%ED%83%80%EC%9E%84_%EB%A9%94%EC%8B%9C%EC%A7%95_%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C]은 Adobe Systems에서 개발한 실시간 스트리밍 전송 프로토콜로, 오디오·비디오 데이터를 빠르게 전송할 수 있습니다. YouTube Live, Facebook Live, Twitch 등 주요 플랫폼에서 널리 사용되고 있으며, 라이브 방송과 실시간 통신에 적합합니다.) 또한 카메라 필터, 팝업 윈도우 모드, 음소거(Mute), 밝기 조절, 화면 반전 등 다양한 부가 기능을 지원하며, 당사의 라이브 커머스 서비스 [https://www.catenoid.net/ko/commerce/live-commerce.php]에도 적용되고 있습니다. [https://cdn-images-1.medium.com/max/999/1*y7cZ7z280uNBRCUKvugD7g.png]Kollus 라이브 인코더 구조Kollus 라이브 인코더는 크게 UI 와 서비스 형태로 구성되어 있으며 서비스는 SDK를 통해 제공합니다. UI와 SDK 간에는 KollusLive Class을 통해서 API 형태로 기능을 제공합니다. 앱은 제어와 UI를 담당하고 인코더 서비스는 인코딩과 전송을 처리하는 구조로, 안정적인 실시간 방송을 구현할 수 있습니다. 라이브 인코더 동작 방식 [https://cdn-images-1.medium.com/max/1024/1*TRmw5bvKcvPbzTuCVV4onw.png]라이브 인코더 동작 방식안드로이드 환경에서 Kollus 라이브 인코더를 통해 RTMP 방송이 송출되는 과정은 크게 네 단계로 나눌 수 있습니다. 1. 초기화 앱이 Init API를 호출하면 카메라 설정 및 Surface 정보가 인코더 서비스로 전달됩니다. 서비스는 카메라를 열고 설정을 적용한 뒤, 프리뷰 화면을 실행하여 사용자가 방송 준비 상태를 확인할 수 있도록 합니다. 2. 방송 시작 앱에서 StartBroadcasting API를 호출하면 인코더 서비스는 RTMPMuxer를 통해 서버와 연결을 시도합니다. * RTMP 연결(TCP)을 거쳐 스트리밍 세션을 초기화합니다. * 서버가 준비되지 않았거나 네트워크 문제가 발생하면 RTMP Open Error를 앱으로 전달하여 예외 처리와 사용자 피드백이 가능하도록 설계되어 있습니다. 3. 데이터 전송 연결이 정상적으로 완료되면 인코더는 비디오·오디오 데이터를 인코딩 후 송출합니다. * VideoEncoder: 카메라 프리뷰 Surface에서 비디오 데이터를 수집하고, 안드로이드 MediaCodec 하드웨어 가속을 활용해 video/avc 포맷으로 인코딩합니다. * AudioEncoder: 마이크 입력을 받아 audio/mp4a-latm 포맷으로 인코딩한 뒤, RTMPMuxer를 통해 서버로 전송합니다. 4. 방송 종료 (STOP) 앱이 StopBroadcasting API를 호출하면 인코더 서비스는 서버에 RTMP Close 요청을 보내고 데이터 송출을 종료합니다. 인코더 앱 주요 기능 [https://cdn-images-1.medium.com/max/695/1*RFdUab3R4NIqtZ53dpORgg.png]라이브 인코더 앱 화면Kollus 안드로이드 라이브 인코더는 단순한 RTMP 송출 기능 뿐 아니라 실제 방송 환경에서 활용할 수 있는 다양한 기능을 제공합니다. 로그인 및 인증 라이브 방송을 만들기 위해서는 라이브 콘솔에서 제공하는 Creator 코드와 Creator 스트림 키로 인증한 후 안전하게 송출할 수 있습니다. 카메라 제어 카메라 프리뷰의 해상도 조절, 줌 인/아웃, 플래시 제어 등 기본적인 카메라 컨트롤 기능을 지원합니다. 화면 반전 영상 출력을 상하 또는 좌우 반전시켜 다양한 촬영 환경에 대응할 수 있습니다. 팝업 윈도우 모드 송출 도중 앱이 일시 정지(Pause)되더라도 팝업 윈도우 모드를 통해 방송을 지속할 수 있습니다. 화면 컨트롤 영상 밝기 조절 및 좌우 반전 기능으로 송출되는 화면을 유연하게 제어할 수 있습니다. 영상 필터 Natural, Sepia, Vignette, Mono, Posterize 등 다양한 필터 효과를 적용할 수 있습니다. 음성 차단 영상은 그대로 송출하면서 음성만 차단하여 무음 방송을 지원합니다. 음성만 송출 영상 없이 오디오만 RTMP 스트리밍으로 전달할 수 있어, 음성 중심의 콘텐츠 제작에도 활용 가능합니다. [https://cdn-images-1.medium.com/max/360/1*c_7TcwHbVc4p0iXDGfIocQ.png]ONAIR 표시 송출이 시작되면 진행 시간을 화면에 표시하여, 방송자가 현재 방송 상태와 송출 시간을 쉽게 확인할 수 있습니다. 네트워크 상태 표시 송출 지연이나 전송 실패가 발생할 경우, 네트워크 상태 바를 통해 실시간 송출 상태를 확인할 수 있습니다. 이를 통해 네트워크 품질 문제에 빠르게 대응할 수 있습니다. 수동 녹화 지원 서버의 수동 녹화 설정 여부를 체크한 뒤, 앱에서 녹화 기능을 활성화할 수 있습니다. 필요한 경우 송출과 동시에 녹화를 진행하여 콘텐츠 아카이빙에 활용할 수 있습니다. 기술 구현 특징 라이브 인코더를 구현하기 위해 사용하였던 기술 중 일부를 관련된 소스 코드와 함께 설명하도록 하겠습니다. MEDIACODEC을 활용한 인코딩 설정 안드로이드 MediaCodec 을 사용하여 비디오와 오디오 인코더를 설정하는 예시 코드 입니다. VIDEOENCODER 예시 MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, width, height); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameInterval); AUDIOENCODER 예시 MediaFormat audioFormat = MediaFormat.createAudioFormat( AUDIO_MIME_TYPE, sampleRate, channelCount); audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize); mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE); mAudioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); OPENGL 기반 SURFACETEXTURE 생성 및 필터 효과 적용 안드로이드에서 라이브 스트리밍 시 카메라 영상을 실시간으로 가공하기 위해 OpenGL을 활용할 수 있습니다. SurfaceTexture와 쉐이더 프로그램을 조합하면 밝기 조절이나 흑백 처리 같은 다양한 필터 효과를 구현할 수 있습니다. 1. SURFACETEXTURE 생성 GlUtil.createProgram() 에서 쉐이더 프로그램을 생성하여 mProgramHandle을 반환받고, 이후 onDrawFrame()에서 사용합니다. mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; FRAGMENT_SHADER_EXT = getNaturalShader(); mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT); 2. OPENGL TEXTUREID 생성 및 바인딩 Texture 객체를 생성하고 OpenGL 파라미터를 설정합니다. public int createTextureObject() { int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); GlUtil.checkGlError("glGenTextures"); int texId = textures[0]; GLES20.glBindTexture(mTextureTarget, texId); GlUtil.checkGlError("glBindTexture " + texId); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GlUtil.checkGlError("glTexParameter"); return texId; } 이후 생성된 TexId를 이용하여 GLSurfaceView.Renderer에 사용할 SurfaceTexture를 생성합니다. CameraSurfaceRenderer 를 이용하여 카메라 프리뷰 영상을 표시하여, UI에서 전달받은 GLSurfaceView에 설정하여 CameraSurfaceRenderer을 통하여 OpenGL Texturer가 사용될 수 있도록 설정합니다. mSurfaceTexture = new SurfaceTexture(mTextureId); mRenderer = new CameraSurfaceRenderer(mCameraHandler, sVideoEncoder, Context); mGLView = glView; mGLView.setRenderer(mRenderer); 3. SHADER PROGRAM 기반 필터 효과 getNaturalShader()는 필터가 적용되지 않은 기본 프로그램을 반환하며, 모바일에서는 GL_OES_EGL_image_external이 설정되어야 합니다. main() 안의 texture2D()에서 실제 OpenGL의 texture 설정이 적용되며, uBrightness 변수가 선언되었고, gl_FragColor값을 변경하여 영상의 밝기가 조절 가능합니다. public String getNaturalShader() { String shader = "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "uniform samplerExternalOES sTexture;\n" + "uniform float uBrightness;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + " gl_FragColor = uBrightness * color;\n" + "}\n"; return shader; } 4. 흑백 필터 RGB 값을 가중합(dot 연산)하여 흑백으로 변환하는 Shader입니다. dot()에 color 값을 지정하여 gl_FragColor 에 적용하여 필터 효과가 나타납니다. public String getGrayScaleShader() { String shader = "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "uniform samplerExternalOES sTexture;\n" + "uniform float uBrightness;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + " float y = dot(color, vec4(0.299, 0.587, 0.114, 0));\n" + " gl_FragColor = uBrightness * vec4(y, y, y, color.a);\n" + "}\n"; return shader; } 5. 밝기 값 적용 실제 렌더링 시에는 uBrightness 위치를 얻어와 원하는 밝기 값(mPreviewBrightness)을 적용합니다. onDrawFrame()시 mBrightnessLoc 변수에 mPreviewBrightness을 설정하여 영상 밝기에 대한 필터 효과를 동적으로 적용 가능합니다. mBrightnessLoc = GLES20.glGetUniformLocation(mProgramHandle, "uBrightness"); GlUtil.checkLocation(mBrightnessLoc, "uBrightness"); ... GLES20.glUniform1f(mBrightnessLoc, mPreviewBrightness); 마무리하며 Kollus 안드로이드 라이브 인코더는 RTMP 기반의 안정적인 송출을 지원하며 카메라 제어, 필터 등 다양한 부가 기능을 제공하여 실제 서비스 환경에서 활용하기 적합합니다. 특히 MediaCodec과 OpenGL을 활용한 인코딩 및 필터 구현 방식은 개발자에게 유용한 참고 자료가 될 수 있습니다. 앞으로도 고객 요구와 모바일 환경 변화에 맞추어 기능을 확장해 나갈 예정입니다. 많은 이용 부탁 드립니다. 비디오 스트리밍 서비스를 선도하는 카테노이드는 수백만 사용자에게 끊김 없는 경험을 전달하는 기술, 그 도전을 함께할 전문가를 기다립니다! 열려 있는 채용 공고에 대해서는 당사 채용 페이지 [https://catenoid.career.greetinghr.com/ko/home]를 참조해 주세요. [https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a6e397480119] Kollus 안드로이드 라이브 인코더 소개 [https://techblog.catenoid.net/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C-%EC%9D%B8%EC%BD%94%EB%8D%94-%EC%86%8C%EA%B0%9C-a6e397480119] was originally published in Catenoid TechBlog [https://techblog.catenoid.net] on Medium, where people are continuing the conversation by highlighting and responding to this story.