블로그 이미지
우주청년
집구석 음악가, 잡식성 프로그래머

calendar

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          

Notice

2011.04.05 19:37 문서/프로그래밍

이제 로컬 동영상 재생과 자막 출력이 끝났으니, 스트리밍!!을 시작해 봅시다.
그리고 다른 어플에서 암시적 인텐트로 우리가 만든 플레이어가 동작하게 만들어 봅시다.

[할일]
 - 파일 리스트 꾸미기
 - 로컬 동영상 재생 + 로컬 자막

 - 동영상 스트리밍 + 외부 자막
 - 세부 조절 옵션 넣기
 - 에러 잡기

[본문]

  단지 동영상을 스트리밍 하는 부분은 VideoView가 지원하므로 건드릴 부분이 없습니다. 하지만 우리는 웹 브라우저의 동영상 링크나, 다른 어플에서 동영상을 선택했을때 우리의 동영상 플레이어에서 해당 동영상을 받아와서 재생하게 할 것 입니다. 세부적인 내용으로는,

1. 동영상 재생 엑티비티의 인텐트 필터 수정
2. 암시적 인텐트 값 받아오기
3. 웹에서 자막 파일 읽어오기
4. 웹에서 동영상 스트리밍 하기

이렇게 4가지 단계로 진행하게 됩니다.
[참고용 소스파일과 어플 설치파일]


1. 동영상 재생 엑티비티의 인텐트 필터 수정

  매니페스트의 xml을 열어서 동영상 엑티비티의 인텐트 필터를 추가합니다.
<activity android:name=".video" android:label="@string/app_name" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:configChanges="keyboardHidden|orientation" android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"></action>
                <category android:name="android.intent.category.DEFAULT"></category>
                <data android:mimeType="video/*"></data>
            </intent-filter>
            <intent-filter>
				<action	android:name="android.intent.action.VIEW"></action>
				<category android:name="android.intent.category.DEFAULT"></category>
				<category android:name="android.intent.category.BROWSABLE"></category>
				<data android:scheme="rtsp"></data>
			</intent-filter>
			<intent-filter>
				<action android:name="android.intent.action.VIEW"></action>
				<category android:name="android.intent.category.DEFAULT"></category>
				<category android:name="android.intent.category.BROWSABLE"></category>
				<data android:scheme="http"></data>
				<data android:mimeType="video/mpg"></data>
				<data android:mimeType="video/avi"></data>
				<data android:mimeType="video/asf"></data>
				<data android:mimeType="video/wmv"></data>
				<data android:mimeType="video/mp4"></data>
				<data android:mimeType="video/mkv"></data>
				<data android:mimeType="video/m4v"></data>
				<data android:mimeType="video/3gp"></data>
			</intent-filter>
        </activity>

그리고 인터넷에 접근 할 수 있게 퍼미션도 추가해 줍시다.
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>

이걸로, 외부 어플에서 우리의 동영상 플레이어를 호출 할 수 있게 되었습니다.
퍼미션이나 인텐트 필터에 대해서는 안드로이드 개발자 싸이트를 참고하세요. 영어지만 익숙해져야 합니다.


2. 암시적 인텐트 값 받아오기

  위에서 작성한 xml로 동영상 플레이어가 외부 어플에서 호출 할 수 있게 되었지만, 데이터 값을 넘겨받는 부분은 아직 만들지 않았습니다. 우리는 저번 시간에 만든 video클래스를 수정해서 값을 받아 올 것 입니다.
  우선 저번 시간에 선언했던 변수들 밑에 몇개 더 추가 해 줍시다.
	private boolean useSmi;
	private boolean useWeb;
	private int countSmi;
	private String path; 
	private Uri iPath;
	private File smiFile;
	private URL iSmiPath;

  클래스 내의 지역 변수로 선언되었던 몇개의 변수를 전역으로 바꿨습니다.
  이제 외부에서 넘어온 값을 읽어 봅시다. 저번시간에 main 엑티비티에서 넘어온 값을 처리하는 부분에 몇가지 추가를 합니다.
Intent intent = getIntent();
        if(intent.hasExtra("FilePath")) {
        	path = intent.getStringExtra("FilePath");
        	useWeb = false;
        } else {
        	if(intent.getData().getScheme().equals("http")) {
        		iPath = intent.getData();
        		useWeb = true;
        	} else {
        		path = intent.getData().getPath();
        		useWeb = false;
        	}
        }

  만약 main 엑티비티에서 값이 넘어왔으면 그 경로를 이용해서 로컬 재생을 하고, 아니면 암시적 인텐트로 넘어온 값을 분류해서 스트리밍 하느냐, 로컬 재생을 하느냐를 결정합니다.


3. 웹에서 자막 파일 읽어오기

  만약 암시적 인텐트로 넘어온 값이 웹에서 온 값이면, 해당 주소에서 자막 파일을 얻어 와야 합니다. 저번 시간에 작성했던 "Smi 자막 파일 읽어오기" 부분을 수정해 줍시다.
if(useWeb == false) {
	        String smiPath = path.substring(0,path.lastIndexOf(".")) + ".smi";
	        smiFile = new File(smiPath);
	        
	        if(smiFile.isFile() && smiFile.canRead()) {
	        	useSmi = true;
	        } else {
	        	useSmi = false;
	        }
        } else {
        	String temp = iPath.toString().substring(0,iPath.toString().lastIndexOf(".")) + ".smi";
        	try {
				iSmiPath = new URL(temp);
				useSmi = true;
			} catch (MalformedURLException e) {
				useSmi = false;
			}
        }
        
        parsedSmi = new ArrayList<smiData>();
	    try {
	    	BufferedReader in;
	    	if(useWeb == false) {
	    		in = new BufferedReader(new InputStreamReader(
	    			new FileInputStream(new File(smiFile.toString())),"MS949"));
	    	} else {
	    		in = new BufferedReader(new InputStreamReader(iSmiPath.openStream(),"MS949"));
	    	}
	
    		String s;
    	    long time = -1;
    	    String text = null;
    	    boolean smistart = false;
    	    
    	    while ((s = in.readLine()) != null) {
    	    	if(s.contains("<SYNC")) {
    	    		smistart = true;
    	    		if(time != -1) {
    	    			parsedSmi.add(new smiData(time, text));
    	    		}
    	    		time = Integer.parseInt(s.substring(s.indexOf("=")+1, s.indexOf(">")));
    	    		text = s.substring(s.indexOf(">")+1, s.length());
    	    		text = text.substring(text.indexOf(">")+1, text.length());
    	    	} else {
    	    		if(smistart == true) {
    	    			text += s;
    	    		}
    	    	}
    	    }
    	    
    	    if(smistart == false) {
    	    	useSmi = false;
    	    }
    	    in.close();
    	} catch (IOException e) {
    	    System.err.println(e);
    	    System.exit(1);
    	}

  로컬 동영상이면 해당 경로를 분리해서 .smi 파일의 경로로 수정하고, 웹에 있는 동영상이면 해당 경로를 수정해서 URL 형 변수에 집에 넣습니다.
  그리고 스트림 리더에서 로컬파일을 읽을지, URL 형 변수의 openStream()을 이용해서 웹에 있는 파일을 읽어올지 결정합니다. 다른 부분은 저번에 작성한 것과 동일합니다.


4. 웹에서 동영상 스트리밍 하기

  이제 웹에 있는 자막 불러오기도 완료되었으니, VideoView에 웹에 있는 동영상을 스트리밍 하는 부분을 추가해 봅시다. 저번에 작성했던 "동영상 재생하기" 부분을 수정합니다.
        MediaController mediaController = new MediaController(this);
        mediaController.setAnchorView(videoView);

        videoView.setMediaController(mediaController);
        if(useWeb == false) {
        	videoView.setVideoPath(path);
        } else {
        	videoView.setVideoURI(iPath);
        }
        videoView.requestFocus();
        videoView.start();

추가 된 부분은 비디오뷰의 setVideoPath() 부분을 웹에서 왔으면 setVideoURI로 바꿔준 것 밖에 없습니다. 비디오뷰에서 스트리밍 되는 동영상은 알아서 스트리밍을 해주니, 우리가 해야할 부분은 없습니다. 참 쉽죠?


[마치면서]

  이렇게 동영상 스트리밍과 웹에서 자막 파일 읽어오기가 끝났습니다. 다음 시간에는 메뉴를 이용해서 세부 조절 옵션을 넣어 보겠습니다. 싱크값 수정이나, 자막의 언어 선택, 자막 글자의 크기/위치 등을 조절 할 수 있게 만들겁니다.
  오늘 포스트의 자세한 내용은 첨부한 소스파일을 참고하시고, 궁금한 점이나 문의할 내용은 댓글로 달아주세요.
posted by 우주청년

댓글을 달아 주세요

  1. JinC 2011.04.07 09:24  Addr Edit/Del Reply

    너무 많은 도움이 되고 있습니다. 정말 감사합니다..^^

  2. yh 2011.04.10 00:34  Addr Edit/Del Reply

    동영상을 맛폰에서 보려고 베리즈웹쉐어프로그램으로 제 컴퓨터에 있는 동영상을 공유시켰습니다, 기본적으로 맛폰에서는 AVI파일이 스트리밍 되지 않는다는걸 알았습니다. 그래서 "락플레이어"어플을 받았는대 스트리밍은 되지만 자막이 SRT자막을 지원하는데 SMI자막밖에 없고 또한 스트리밍은 되지만 외부자막을 불러오지는 못하는와중에 이곳에 오게됬네요, 위의 어플을 받았는데 웹상에서 제가 공유시킨 동영상을 열려고하면 스트리밍이 안되고 일반적으로 다운을 받고 보게되는 방식이 됩니다. 스트리밍이 되는건가요?

    • Favicon of https://gsroom.tistory.com 우주청년 2011.04.10 05:00 신고  Addr Edit/Del

      스트리밍 서버나 특별한 코드를 사용하지 않는 한, avi파일은 스트리밍이 되지 않습니다. 그래서 웹 브라우저의 기본동작이 다운로드로 되어 있구요. 동영상 파일을 mp4파일로 변환을 하시거나, RockPlayer 대신 VitalPlayer를 사용하시는 것을 추천합니다. 여기 링크된 어플은 사용자 보다는 입문 개발자들을 위해 팁으로 올려놓은거라 완성도 면에서 많이 떨어집니다.

  3. yh 2011.04.10 18:13  Addr Edit/Del Reply

    아, 그렇군요.. 저는 개발자도 아니고 그렇다고 제 전공은 설계,디자인이라서 개발은 커녕 쓰는것도 벅찰것 같습니다.
    rockplayer처럼 AVI도 스트리밍되면서 동일 웹주소상에있는 SMI자막도 불러올수있게 어플들이 개발됬으면 좋겠습니다.
    vitalplayer도 다운받아봤지만 AVI파일은 스트리밍이 안되네요, 사실상 세컨드라이브같은 웹하드에 올리면 세컨드라이브어플로 자신이 올린 동영상은 어떤 포멧형식이든 다 스트리밍되서 자막과 함께 볼수있지만, 그럴려먼 세컨드라이브 서버에 올려야되는 불편한 일이 생겨서요.ㅎㅎ

    • Favicon of https://gsroom.tistory.com 우주청년 2011.04.10 20:16 신고  Addr Edit/Del

      음.. 일단 웹에있는 .avi 파일도 플레이어에서 받아오게 수정을 해봤는데요.
      재생 안되는 파일이 대부분일 것 같네요.
      http://www.gsnc.kr/GNCPlayer.apk

  4. LeeMong 2011.05.02 14:26  Addr Edit/Del Reply

    우청님 섭정리하심요??? 왜안디요??

  5. Hyo 2011.05.27 15:12  Addr Edit/Del Reply

    정말 잘 읽었습니다. 도움도 많이 되었구요..
    근데 해당 소스 사용해도 되는건가요?? 저작권.. 요런거 혹시 있는건가용?? ^^;;

  6. Hyo 2011.05.31 18:58  Addr Edit/Del Reply

    감사합니다.. 잘 사용하겠습니다. ^^

  7. werpko 2011.07.30 20:37  Addr Edit/Del Reply

    안녕하세요.
    자막과 동영상 싱크 조절하는 부분 이진 검색으로 하셨는데... 더 좋은 방법 없을까요?
    복합 자막에서 같은 시간이 두개일때 깨져버리더라구요 ^^;;

    여기있는 싱크 맞추는 함수를 이용했거든요 ^^;;

    • Favicon of https://gsroom.tistory.com 우주청년 2011.07.30 22:53 신고  Addr Edit/Del

      자막 파싱하는 부분에서 P Class=xxCC 를 얻어와서
      CC별로 배열에 추가해주고,
      2진 검색으로 싱크를 맞추는 부분에서
      사용자가 선택한 CC만 출력해주시면 됩니다.

  8. werpko 2011.07.31 23:52  Addr Edit/Del Reply

    안녕하세요 우주청년님 실력이 모자라서 님에 말씀을 잘 이해를 못하겠네요 ^^;;;
    P Class == xxCC가 무슨뜻인지... 그리고 사용자가 선택한 CC만 출력해준다면 된다는데..P Class 나 xxCC가 무슨뜻인지 모르겠습니다...

    조금더 자세히 설명해주시면...안될까요?;;;

    저는 public int getSyncIndex(long playTime) 이 함수를 이용하는데요.

    님처럼 300millisecond로 통신하면서 싱크를 맞추고 있습니다. 그런데 복합자막에서 한글하고 영어하고 동시에 출력해줘야 되기 때문에 시간이 같은데 그럴 경우 위에 함수를 그대로 사용하면 무한 루프에 빠져버리길래 바꿔서 사용할려고 하는데... 잘 안되네요;;;

    아 그리고 님 함숭에서는 조건문이
    if (parsedSmi.get(m).gettime() <= playTime &&
    playTime < parsedSmi.get(m+1).gettime()) {
    return m;
    }
    if (playTime > parsedSmi.get(m + 1).gettime()) {
    l = m + 1;
    } else {
    h = m - 1;
    }

    이렇게 되어 있는데 저는
    if(sr.subtitleList.get(m).getmTime() <= playTime && playTime < sr.subtitleList.get(m+1).getmTime()) {
    return m;
    }
    else if(playTime >= sr.subtitleList.get(m+1).getmTime()) {
    l=m+1;
    } else {
    h=m-1;
    }
    이렇게 바꿧는데 큰 차이가 없나 해서요...
    질문이 길어서...죄송해요 ㅋ;;

    혹시 자세히 알려주실수 있으시면 werpko123@naver.com으로 메일좀 주시면 안될까요;;
    댓글로 남기셔도 제가 매일 매일 확인하겠습니다 수고하세요 ㅎ

    • Favicon of https://gsroom.tistory.com 우주청년 2011.08.01 00:01 신고  Addr Edit/Del

      음.. smi 포멧에 대해서 알아 보시면 쉬울거에요.

      smi로 된 자막은 다중 언어를 지원하고,
      그 구분을 <P Class = "xxCC"> 로 하거든요
      KRCC면 한국어, ENCC면 영어, JNCC면 일본어.. 이런 식으로요

      그래서 처음에 자막을 파싱할 때,
      KRCC면 한국어 자막 배열에 텍스트와 시간,
      ENCC면 영어 자막 배열에 텍스트와 시간을 넣고

      자막 출력 쓰래드에서
      둘중 하나를 출력해주시면 됩니다.
      그렇게 하면 시간이 겹칠 일은 없겠죠.

  9. 2011.08.04 18:17  Addr Edit/Del Reply

    비밀댓글입니다

  10. Lee 2012.03.09 14:49  Addr Edit/Del Reply

    우주청년님...잘 보고 잘 따라하고 있습니다.
    그런데.외부 (갤러리)에서 동영상 실행하면... 응용프로그램을 선택해서 플레이 하는데...
    실행하다가... 오류가 나며.. 실행이 안되네요.
    웹에서나, 아스트로 / 파일 탐색기에서 파일 선택하여 실행시에...

    "Unable to start activity ComponentInfo .." 에러 메시지가 나오네요.

    이런 문제는 어떤 식으로 풀어야 할까요?

    • Favicon of https://gsroom.tistory.com 우주청년 2012.03.09 19:23 신고  Addr Edit/Del

      해당 엑티비티가 메니페스트에 등록되어있는지 확인해보시고,
      등록되어 있다면 프로젝트를 clean 해준 다음에 다시 빌드해보세요~

      컴포넌트를 확장해서 사용했다면, 모든 생성자를 오버라이딩 해줬는지 확인해 보시구용

  11. Lee 2012.03.12 10:34  Addr Edit/Del Reply

    드뎌 되엇습니다...클린하고...나서 다시 하니 되는 것 같습니다.
    음..먼가...지저분한 것들이 잇었나 봅니다..ㅎㅎ
    동작 잘 하네요..
    우주청년님께서는 세부 기능 만들 계획이 없으신가요..?ㅎ

    • Favicon of https://gsroom.tistory.com 우주청년 2012.03.12 16:00 신고  Addr Edit/Del

      이미 안드로이드에는 소울무비같은 훌륭한 동영상 재생 어플이 많이 있지요.
      하지만, 언젠가 시간이 되면 세부기능 부분과 jni로 지원하지 않는 코덱을 사용하는 방법을 강좌로 한번 적어보고 싶네요 ㅎㅎ

  12. 2013.03.05 16:34  Addr Edit/Del Reply

    비밀댓글입니다

    • Favicon of https://gsroom.tistory.com 우주청년 2013.03.14 11:40 신고  Addr Edit/Del

      강좌에서는 Html.fromHtml() 을 이용해서 출력합니다.
      <br> <i> <font> 같은 태그는 자동으로 처리됩니다.
      smi에서 내용만 가져오시려면 "<" ">"사이에 있는걸 다 없애면 됩니다.

  13. 2013.05.23 10:03  Addr Edit/Del Reply

    비밀댓글입니다

  14. 정말 감사합니다. 공부에 많이 도움이 되었습니다 !

  15. 2015.10.01 01:56  Addr Edit/Del Reply

    비밀댓글입니다