저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의

안드로이드의 MediaPlayer가 스트리밍 프로토콜로 RTSP를 지원하지만 RTSP를 사용해서 미디어를 플레이해보면, 스트리밍이 얼마나 불안정한지 알 수 있다. 안드로이드 3 부터는 HLS를 지원하다고는 하지만 실제로 해보면 기기에 따라 지원이 안 되는 경우도 있다. 그리고, 여전히 2.2~2.3 버전의 기기를 사용하는 사람들도 아직은 상당수 존재할 것 같다. 이런 이류로 예전에 구현 작업을 진행할 때 VOD를 플레이하기 위해 RTSP 대신 HTTP PDL(Progressive download) 방식을 사용했지만, 여전히 LIVE 방송 같은 것을 구현하려면 RTSP를 사용해야했다.


필자가 FFmpeg을 사용해가면서 할 만큼 안드로이드만 집중적으로 팔 수 있는 상황이 아니였기에 포기하고 있었는데, 검색을 하던 중 우연히 Vitamio라는 안드로이드 라이브러리를 알게 되었다. Vitamio는 ARM 기반 프로세스를 위한 ffmpeg 모듈과 이를 이용한 미디어 플레이어 기반 코드를 제공하는 안드로이드 라이브러리로서, 이를 사용하면 비교적 쉽게 안드로이드 기기에서 RTMP, HLS 등의 스트리밍을 플레이할 수 있다. 물론, HTTP PDL도 지원한다.


Vitamio는 안드로이드 2.1 이상을 지원하고 ARMv6, VFP, ARMv7, NEON 등을 지원하기 때문에 현재 시중에 나온 대다수의 안드로이드 기기에서 동작한다.


Vitamio 사용을 위한 개발 환경 설정


아주 간단하다. 다음의 순서대로 진행하면 된다.

  1. http://vitamio.org/vitamios/android-3-dot-0?locale=en 에서 3.0 버전을 다운로드 받는다. (3.0 버전 기준)
  2. 압축받은 파일을 풀면 VitamioBundle 폴더와 VitamioDemo 폴더가 생긴다.
  3. 이클립스에서 VitamioBundle을 안드로이드 프로젝트로 임포트한다.
    1. 프로젝트 이름이 InitActivity로 임포트 되는데, VitamioBundle로 바꿔준다. (안 바꿔줘도 상관은 없다.)
  4. 안드로이드 프로젝트를 생성한다. A프로젝트라고 하자.
  5. A 프로젝트 선택 후, [Project 메뉴] -> [Properties] 메뉴 실행
    1. [Android] 항목 -> 레퍼런스 프로젝트에 VitamioBundle를 추가
    2. [Project References] 항목 -> VitamioBundle 추가
  6. A프로젝트의 AndroidManifest.xml 파일에 다음 Activity 설정을 추가한다.
  7. <activity

    android:name="io.vov.vitamio.activity.InitActivity"

    android:launchMode="singleTop"

    android:theme="@android:style/Theme.NoTitleBar"

    android:windowSoftInputMode="stateAlwaysHidden" />

  8. 이제 A프로젝트에서 Vitamio가 제공하는 VideoView, MediaPlayer 등을 이용해서 구현하면 된다.

Vitamio는 안드로이드가 기본으로 제공하는 VideoView, MediaPlayer 등과 (패키지만 다른) 동일한 이름의 클래스를 제공하고 있다. 따라서, 기존에 안드로이드의 미디어 관련 기능을 사용하고 있다면, 아주 작은 코드 수정만으로도 Vitamio의 기능을 사용할 수 있다. (다운로드 배포판에 함께 포함된 VitamioDemo에 사용코드 예제가 포함되어 있으니 참고하기 바란다.)


Vitamio가 제공하는 기능을 사용하려면 최초에 네이티브 라이브러리를 로딩하는 과정을 거쳐야 하는데, 이 과정은 다음의 코드를 사용하여 처리한다.


import io.vov.vitamio.LibsChecker;


public class MainActivity extends Activity {


@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (! LibsChecker.checkVitamioLibs(this)) {

return;

}


따라서, 앱이 실행될 때 최초에 한 번 위 코드를 실행해 주어야 Vitamio가 제공하는 기능을 올바르게 사용할 수 있다.


VitamioBundle 프로젝트에 대한 레퍼런스 없애기


VitamioBundle에 대한 의존을 하려면 함께 하는 개발자들의 이클립스 프로젝트 VitamioBundler을 임포트 해 주어야 하는 불편함이 있다. 여럿이 공동으로 작업을 한다면 VitamioBundler을 사용하지 않고 작업할 수 있으면 더 좋을 것이다.


이를 위한 방법은 역시 간단하다. 다음의 순서대로 진행하면 된다. (참고로, 아래의 코드들은 VitamioBundle을 이용해서 개발하고 있는 OPlayer의 소스 코드 http://code.taobao.org/p/oplayer/src/trunk/OPlayer/ 에서 참고한 것이다.)

  1. 안드로이드 프로젝트의 libs 폴더에 다음의 파일을 복사한다.
    1. VitamioBundle/lib/vitamio.jar
    2. VitamioBundle/lib/armeabi 폴더 및 armeabi-v7a 폴더
  2. 안드로이드 프로젝트의 res/raw 폴더에 다음의 파일을 복사한다.
    1. VitamioBundle/res/raw/libarm.so
  3. io.vov.vitamio 패키지를 생성하고 그 곳에 R.java 클래스를 생성한다. [소스 코드는 아래 참고]
  4. [프로젝트패키지].vitamio 패키지에 다음의 두 클래스를 생성한다.
    1. LibsChecker.java [소스 코드는 아래 참고]
    2. InitActivity.java [소스 코드는 아래 참고]
  5. AndroidManifest.xml 파일에 Vitamio의 InitActivity가 아닌 4-2 과정에서 생성한 InitActivity를 이용해서 액티비티 설정을 추가한다.
그 다음에 Vitamio에 포함된 LibsChecker가 아닌 위 과정에서 생성한 LibsChecker를 이용해서 초기화작업을 진행하면 된다.

import [마이패키지].vitamio.LibsChecker;

public class MainActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!LibsChecker.checkVitamioLibs(this)) {
return;
}

앞서 과정에서 소개한 각 코드는 다음과 같다.

- R.java
package io.vov.vitamio;

public class R {
public static final class raw {
public static final int libarm = [마이패키지].R.raw.libarm;
}
}

- InitActivity.java
/*
 * Copyright (C) 2012 YIXIA.COM
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package [마이패키지].vitamio;

import io.vov.vitamio.Vitamio;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.WindowManager;

public class InitActivity extends Activity {
public static final String FROM_ME = "fromVitamioInitActivity";
public static final String EXTRA_MSG = "EXTRA_MSG";
public static final String EXTRA_FILE = "EXTRA_FILE";
private ProgressDialog mPD;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

new AsyncTask<Object, Object, Object>() {
@Override
protected void onPreExecute() {
mPD = new ProgressDialog(InitActivity.this);
mPD.setCancelable(false);
mPD.setMessage("Initializing decoders...");
mPD.show();
}

@Override
protected Object doInBackground(Object... params) {

Vitamio.initialize(getApplicationContext());
uiHandler.sendEmptyMessage(0);
return null;
}
}.execute();
}

private Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mPD.dismiss();
Intent src = getIntent();
Intent i = new Intent();
i.setClassName(src.getStringExtra("package"),
src.getStringExtra("className"));
i.setData(src.getData());
i.putExtras(src);
i.putExtra(FROM_ME, true);
startActivity(i);

finish();
}
};
}

- LibsChecker.java
package [마이패키지].vitamio;

import android.app.Activity;
import android.content.Intent;
import io.vov.vitamio.Vitamio;

public final class LibsChecker {
public static final String FROM_ME = "fromVitamioInitActivity";

public static final boolean checkVitamioLibs(Activity ctx) {
if ((!Vitamio.isInitialized(ctx))
&& (!ctx.getIntent().getBooleanExtra("fromVitamioInitActivity",
false))) {
Intent i = new Intent();
i.setClassName(ctx.getPackageName(),
"com.scgs.vitamio.InitActivity");
i.putExtras(ctx.getIntent());
i.setData(ctx.getIntent().getData());
i.putExtra("package", ctx.getPackageName());
i.putExtra("className", ctx.getClass().getName());
ctx.startActivity(i);
ctx.finish();
return false;
}
return true;
}
}

미디어 플레이하기


가장 쉬운 방법은 VitamioBundle에 포함된 io.vov.vitamio.widget.VideoView 클래스를 사용하는 것이다. VitamioDemo에 포함된 VideoViewDemo 클래스에 VideoView의 사용 예제가 포함되어 있다.


또한, VideoView 소스 코드를 보면 io.vov.vitamio.MediaPlayer를 어떻게 사용하는지 알 수 있으므로, MediaPlayer를 직접 이용해서 자신에 맞는 플레이어 화면을 구현할 수도 있다.




Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 팔팔청춘 2013.03.21 10:05 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 감사드립니다!!

    • 최범균 madvirus 2013.03.25 09:55 신고  댓글주소  수정/삭제

      플레이어 기능 구현에 도움이 되셨으면 좋겠네요.

    • 정한욱 2015.10.11 21:54 신고  댓글주소  수정/삭제

      안녕하세요 Wowza 스트리밍 서버를 통해서 안드로이드 Videoview 로 RTSP URL 받아오려고 하는 초보 개발자 입니다. ㅠㅠ 영상을 보내는데 android 에서 Videoview로 받을때 아예 회색으로 다 깨져버리는 현상이 있는데 어떻게 처리해야하는지 정말로 도움 부탁드립니다. ㅠㅠ 꼭 부탁드리고 귀찮더라도 01094960568연락주시면 감사하겠습니다.

  2. 흐아아앜 2013.06.11 19:30 신고  댓글주소  수정/삭제  댓글쓰기

    demo 코드로 미디어플레이어에서 RTMP 재생을 해봤는데 안되네요

    다른방법을 써야하나요?

  3. 고맙습니다 2013.07.14 16:47 신고  댓글주소  수정/삭제  댓글쓰기

    어떻게 구현할지 막막했는데 좋은 정보 감사합니다!!!
    fms에 있는 동영상을 앱으로 스트리밍 재생하는 연습을 해볼건데 이 라이브러리로 가능한거겠죠??

  4. 지젝 2013.12.09 19:22 신고  댓글주소  수정/삭제  댓글쓰기

    잘 돌아가네요 ^^ 감사합니다.

  5. wind~~~ 2013.12.11 13:37 신고  댓글주소  수정/삭제  댓글쓰기

    감사합니다~
    덕분에 보틀넥 하나를 풀었네요...

    더불어...

    RTSP 서버에 붙을 때 vitamio Fatal signal 11 (SIGSEGV) 라는 에러가 발생할 수 있습니다. 이 때 재생이 안되죠.

    source file 중 MediaPlayerDemo_Video.java에서 mMediaPlayer.getMetadata();를 제거하면 데모를 돌릴 수 있습니다.
    참고 하세요....

    • 정한욱 2015.10.11 21:55 신고  댓글주소  수정/삭제

      안녕하세요 Wowza 스트리밍 서버를 통해서 안드로이드 Videoview 로 RTSP URL 받아오려고 하는 초보 개발자 입니다. ㅠㅠ 영상을 보내는데 android 에서 Videoview로 받을때 아예 회색으로 다 깨져버리는 현상이 있는데 어떻게 처리해야하는지 정말로 도움 부탁드립니다. ㅠㅠ 꼭 부탁드리고 귀찮더라도 01094960568연락주시면 감사하겠습니다.

  6. allday 2014.03.04 11:01 신고  댓글주소  수정/삭제  댓글쓰기

    좋은글 잘 보고 갑니다~

  7. 정한욱 2015.10.11 21:54 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 Wowza 스트리밍 서버를 통해서 안드로이드 Videoview 로 RTSP URL 받아오려고 하는 초보 개발자 입니다. ㅠㅠ 영상을 보내는데 android 에서 Videoview로 받을때 아예 회색으로 다 깨져버리는 현상이 있는데 어떻게 처리해야하는지 정말로 도움 부탁드립니다. ㅠㅠ 꼭 부탁드리고 귀찮더라도 01094960568연락주시면 감사하겠습니다.

  8. minsus 2016.01.07 14:26 신고  댓글주소  수정/삭제  댓글쓰기

    이거 유료 라이브러리죠?

  9. kc 2017.02.24 07:29 신고  댓글주소  수정/삭제  댓글쓰기

    글 잘봤습니다. 감사합니다.ㅋ

    저 하나 질문이 있는데요. vitamio Videoview 및 vitamio Mediaplayer를 다중으로 사용해서 여러 영상을 못 띄우나요?

사이트에 동영상 플레이 기능을 넣고 싶을 때 고려해볼만한 플레이어가 Flowplayer이다. HTML5의 Video 관련 기능이 아직 완전하게 표준화되지 않은 시점이기에, PC 기반의 웹 브라우저에서 동영상을 플레이하는 가장 현실적인 방안은 플래시를 사용하는 것이다. 하지만, 규모가 작은 회사에서 플래시 개발자를 고용하거나 플래시 기반의 동영상 플레이어를 외주 주는 것은 비용 문제 때문에 쉽지가 않다. 이럴 때 바로 유용하게 사용할 수 있는 것이 Flowplayer (http://flowplayer.org/)이다.


Flowplayer는 GPL 3 라이선스를 따르는 오픈 소스로 제공되는 플레이어로서 라이선스를 따르는 한 자유롭게 사용할 수 있다. 또한, 로고를 마음대로 변경할 수 있는 상용 버전도 1개 도메인에 대해 95$ 밖에 안 하기 때문에 매우 싼 값에 플래시 기반의 동영상 플레이어를 사용할 수 있다. 게다가 다양한 플러그인들이 존재해서 필요한 기능들이 이미 존재하는 경우가 많았다. 이런 이유로 필자도 FlowPlayer를 선택하게 되었다.


Flowplayer의 주요 기능


동영상 플레이어니까 당연히 동영상을 재생해주는 기능을 제공하는데, 간략하게 주요 기능들을 정리해보면 다음과 같다.

  • 손쉬운 설치와 설정
  • pseudostreaming 지원 / RTMP 지원 /  YouTube 비디오 재생
  • 플레이어 이벤트를 처리할 수 있는 다양한 자바스크립트 API
  • 플러그인을 통한 손쉬운 기능 확장
  • ABR(Adaptive Bitrate) 지원
  • URL Obfuscation 기능
Flowplayer 자체는 매우 설치하기 쉽다. 다운로드 받은 파일을 웹 서버의 알맞은 곳에 올리고 간단한 자바 스크립트 코드를 이용하면 된다. 아래 코드는 RTMP 기반의 플레이 설정 예이다.


<script src="jquery-1.7.1.js"></script>

<script src="flowplayer-3.2.11.min.js"></script>


<script type="text/javascript">

$(function() {

flowplayer("player", "flowplayer-3.2.12.swf", {

clip: {

url: 'mp4:0000/00/1/1_PC1.mp4',

provider: 'rtmp'

},

plugins:  {

rtmp: {

url: "flowplayer.rtmp-3.2.10.swf",

netConnectionUrl: 'rtmp://wowza.myserver-host.com:1935/vod'

}

}

});

});

</script>

</head>

<body>

<div id="player" style="width: 644px; height: 276px; margin: 0 auto; text-align: center">

</div>

</body>

</html>


모든 설정이 위와 같이 JSON 형식의 자바 스크립트 코드로 이루어지기 때문에, Flowplayer 사이트만 참고하면 비교적 쉽게 동영상 재생 기능을 사이트에 추가할 수 있다.


다양한 이벤트 지원


동영상 재생이 종료되면 사용자에게 추가 정보를 알려주는 레이어를 띄워 주고 싶을 수 있다. 예를 들어, 상품에 대한 리뷰 동영상의 재생이 끝나면 상품에 대한 상세 보기 페이지로 이동할 수 있는 안내 레이어를 보여주고 싶은 경우가 있을 것이다. 이렇게 플레이어의 상태에 따라 특정한 동작을 하고 싶은 경우에는 Flowplayer가 제공하는 이벤트를 사용하면 된다.


플레이어가 제공하는 이벤트는 다음과 같은 것들이 있다.

  • volume: 볼륨 조절시 발생
  • load: 플레이어가 완전히 로딩될 때 발생
  • fullscreen, fullscreenExit: 풀스크린 모드 전환이나 취소시 발생
  • mouseOver, mouseOut: 플레이어 위에 마우스가 올라가거나 벗어날 때 발생
개별 클립에 대한 이벤트로는 다음과 같은 것들이 있다.
  • begin: 클립이 시작될 때 발생
  • finish: 클립이 끝날 때 발생
  • pause: 일시 중지할 때 발생
  • stop: 멈출 때 발생
  • seek: 시간을 이동했을 때 발생
  • cuepoint: 지정한 시간에 발생

위 이벤트에 반응하는 코드는 자바 스크립트를 이용해서 작성할 수 있다. 따라서, 자바 스크립트 코드만 작성할 수 있다면 각 이벤트가 발생할 때 원하는 기능을 수행할 수 있다.


유용한 플러그인들


Flowplayer의 장점 중 하나는 플러그인에 있다. 플래시와 자바 스크립트의 두 종료 플러그인이 존재하는데, 몇 가지 유용한 플러그인을 소개하도록 하겠다.


Content 플러그인


Content 플러그인은 플레이어 화면 위에 원하는 HTML을 보여줄 수 있는 기능이다. 링크 등을 걸 수 있기 때문에, 시청 도중에 관련 상품을 보여준다거나 종료 후 추가 정보를 보여주고 싶을 때 유용하게 사용할 수 있다.


[발췌: Flowpalyer]


Caption 플러그인


Caption 플러그인을 사용하면 자막을 올릴 수 있다. 아래는 Caption 플러그인을 사용해서 자막을 넣은 예를 보여주고 있다.


[발췌: Flowpalyer]


Menu 플러그인


Menu 플러그인을 사용하면 컨트롤바에 메뉴를 위한 버튼이 추가된다. 해당 버튼을 클릭하면 메뉴가 화면에 표시된다. 메뉴 목록은 자바 스크립트 코드를 이용해서 입력할 수 있다. 관련 비디오 목록을 보여주는 용도로 사용할 수 있을 것이다.


[발췌: Flowpalyer]


오픈소스와 플러그인 확장


개발자 입장에서 Flowplayer의 가장 매력적인 점은 오픈 소스이면서 플러그인을 쉽게 확장할 수 있다는 점이다. 컨트롤바부터 모든 것이 플러그인을 처리되기 때문에 플래시 개발 역량을 조금만 키우면 Flowplayer의 플레이 기능을 사용하면서 동시에 원하는 기능을 제공하는 플러그인을 추가할 수 있다.

Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 권남 2012.07.06 19:31 신고  댓글주소  수정/삭제  댓글쓰기

    저희는 FlowPlayer를 사용하다가 HTML5 video + video.js 기반으로 변경하였습니다.
    Flash를 사용하기 보다는 HTML5와 그 fallback(video.js 나 jwPlayer 등)을 저는 권하고 싶습니다.
    MP4 코덱 + HTML5 Video + video.js 로 작업하면 스마트폰, 태블릿, PC 모두 아울러서 거의 모든 현대적인 브라우저에서 재생가능합니다.

    단, 플러그인 등은 당연히 부족합니다.