안드로이드의 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 사용을 위한 개발 환경 설정
아주 간단하다. 다음의 순서대로 진행하면 된다.
- http://vitamio.org/vitamios/android-3-dot-0?locale=en 에서 3.0 버전을 다운로드 받는다. (3.0 버전 기준)
- 압축받은 파일을 풀면 VitamioBundle 폴더와 VitamioDemo 폴더가 생긴다.
- 이클립스에서 VitamioBundle을 안드로이드 프로젝트로 임포트한다.
- 프로젝트 이름이 InitActivity로 임포트 되는데, VitamioBundle로 바꿔준다. (안 바꿔줘도 상관은 없다.)
- 안드로이드 프로젝트를 생성한다. A프로젝트라고 하자.
- A 프로젝트 선택 후, [Project 메뉴] -> [Properties] 메뉴 실행
- [Android] 항목 -> 레퍼런스 프로젝트에 VitamioBundle를 추가
- [Project References] 항목 -> VitamioBundle 추가
- A프로젝트의 AndroidManifest.xml 파일에 다음 Activity 설정을 추가한다.
<activity
android:name="io.vov.vitamio.activity.InitActivity"
android:launchMode="singleTop"
android:theme="@android:style/Theme.NoTitleBar"
android:windowSoftInputMode="stateAlwaysHidden" />
- 이제 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/ 에서 참고한 것이다.)
- 안드로이드 프로젝트의 libs 폴더에 다음의 파일을 복사한다.
- VitamioBundle/lib/vitamio.jar
- VitamioBundle/lib/armeabi 폴더 및 armeabi-v7a 폴더
- 안드로이드 프로젝트의 res/raw 폴더에 다음의 파일을 복사한다.
- VitamioBundle/res/raw/libarm.so
- io.vov.vitamio 패키지를 생성하고 그 곳에 R.java 클래스를 생성한다. [소스 코드는 아래 참고]
- [프로젝트패키지].vitamio 패키지에 다음의 두 클래스를 생성한다.
- LibsChecker.java [소스 코드는 아래 참고]
- InitActivity.java [소스 코드는 아래 참고]
- 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를 직접 이용해서 자신에 맞는 플레이어 화면을 구현할 수도 있다.