티스토리 뷰

반응형

OneDrive앱에 Audio play기능을 추가하려는 생각이 들어서 시작한 작업이 한 2주 정도 걸린 듯하다..3주인가? 음..작업을 하면서 여러가지 자료도 찾아보고 알게된 내용들을 정리 하려고 한다.

 

 

 

 


 

0. 참고자료(msdn의 내용들은 uri 중 en-us를 ko-kr로 변경하면 한글로 볼 수 있습니다.)

****Windows 8.1

How to play audio in the background (XAML)

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj841209.aspx


Supporting background audio in your Windows 8.1 app

http://blogs.msdn.com/b/johnkenn/archive/2013/12/31/supporting-background-audio-in-your-windows-8-1-app.aspx


Stream Online Audio and Play in Background Windows 8.1

https://code.msdn.microsoft.com/windowsapps/Stream-Online-Audio-and-e4fb5ea1#content


Playing sounds in a Universal Windows MVVM app

http://blogs.u2u.be/diederik/post/2014/07/10/Playing-sounds-in-a-Universal-Windows-MVVM-app.aspx


****Windows Phone 8.1

Overview: Background audio (Windows Phone Store apps)

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn642090.aspx


Background Audio for Windows Phone 8.1 Sample

https://code.msdn.microsoft.com/windowsapps/BackgroundAudio-63bbc319



1. Windows 8.1, Windows Phone 8.1, Windows 10은 각각 Background play하는 방식이 다르다.

처음에 Windows 8.1 백그라운드 오디오 관련 문서를 읽고, 셈플을 보면서 따라하니 너무 간단했다. 그래서 ..아 이거 너무 쉬운데..뭔가...내 뒷통수를 칠 것 같은데..라는 불안 감을 가지면서 시작했다.


첫번째 멘붕은 Windows Phone 8.1 백그라운드 오디오 관련 문서를 읽어보고 발생했다.  Windows 8.1과는 전혀 다른 background play 방법을 사용해야 한다는 것이다. 쿨럭.. 거기다, Windows 10은 폰과 비슷한 방법으로 사용하는 것을 알 수 있었다.


두번째는 Windows 8.1에서 MediaElement를 이용해서 백그라운드 오디오 플레이를 하다가 다른 페이지로 네비게이션을 했다가, 다시 돌아오면 그 때부터 백그라운드 오디오 플레이가 않된다... 즉, MediaElement는 반드시 xaml에 들어가 있는 상태로 앱이 시작할 때부터 끝날때까지 하나를 사용해야 한다는 것이다.


세번째는 Windows Phone에서 Wi-Fi가 연결된 상태에서는 백그라운드 오디오 플레이를 하면 오류가 발생한다는 것이다. 파일 다운로드 같은 것은 잘되는데..왜 오디오 플레이를 하는데 오류가 발생하는지..



2. Windows 8.1

2-1. MediaElement를 화면에 넣고 사용, ViewModel에서 인스턴스 시켜서는 사용할 수 없다.

2-2. MediaElement 하나를 처음부터 끝까지 사용해야 한다.

2-3. Windows 10 desktop이랑은 방법이 다르다



3. Windows Phone 8.1

3-1. BackgroundMediaPlayer에 인스턴스된  MediaPlayer를 사용한다.

3-2. IBackgroundTask를 이용해서 백그라운드에서 동작하도록 만들어야 한다.

3-3. Foreground app과 Background Task는 BackgroundMediaPlayer 녀석을 이용해서 메시지를 전송한다.

3-4. Wi-Fi에서 Background audio play를하면 0x80072F30 오류가 발생한다.

3-5. Windows 10 mobile과는 방법이 비슷한 것 같다.



4. Windows 8.1 문제 해결

Background player 기본

MediaElement 속성 중에 AudioCategory라는 녀석을 BackgroundCapableMedia로 설정하고, Package.appxmanifest -> Declarations에서 Background Tasks를 하나 추가하고, Supported task types는 Audio를 선택한다. 마지막으로 Entry point는 앱 이름을 입력한다.

정말 간단하게 처리 완료! 더 자세한 사항은 참고에 있는 링크를 이용하면 된다.


* 위에서 언급했던 이슈를 해결해보자. Windows 8.1 스타일의 앱이기 때문에 모든 화면이 네이게이션이 된다. 이런 상태에서 어떻게 MediaElement컨트롤을 화면에 고정 시켜서 사용할 수 있을까?


정답은 Frame에 끼워 넣어서 사용하는 것이다.

위에 참고 링크 중 Playing sounds in a Universal Windows MVVM app 포스트를 보면 그런 내용이 나온다. 아마 이 아저씨도 네비게이션이 되면 백그라운드 플레이가 않되는 것을 보고 맨붕이 오지 않았을까 생각해본다.


스타일

    <!-- Injecting media players on each page -->
    <Style x:Key="RootFrameStyle" TargetType="Frame">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Frame">
                    <Grid>
                        <MediaElement AutoPlay="False"/>
                        <ContentPresenter />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

 

프레임에 스타일 입력

                //새 프레임 생성

                rootFrame = new Frame {CacheSize = 1};
#if WINDOWS_APP
                //Root Frame에 MediaElement를 추가한다.
                rootFrame.Style = Application.Current.Resources["RootFrameStyle"] as Style;

#endif

                // Place the frame in the current Window

                Window.Current.Content = rootFrame;


 

플레이 화면에 진입하면 초기화 모듈을 실행해서 프레임에 들어가있는 MediaElement 컨트롤을 꺼내서 뷰모델에 넣는다.

        public void Initialize()

        {

            try

            {

                var rootGrid = VisualTreeHelper.GetChild(Window.Current.Content, 0);

                var me = VisualTreeHelper.GetChild(rootGrid, 0) as MediaElement;

                if (me == null) return;

                SetMediaElement(me);

            }

            catch (Exception ex)

            {

                Debug.Assert(false, ex.Message);

            }

        }

 


 

* Uri를 이용한 재생 방법

//me : MediaElement 인스턴스, uri : Mp3파일이 존재하는 Uri 경로
me.Source = new Uri(uri);


 

* LocalFile 재생 방법

//GetLocalFileAsync : IStorageFile을 반환하는 사용자 정의 메소드
//me : MediaElement
var file = await GetLocalFileAsync();
if (file == null) return;

var stream = await file.OpenAsync(FileAccessMode.Read);

me.SetSource(stream, file.ContentType);



* 윈도우 10에서 발생하는 최소화 했을 때 백그라운드 플레이가 중지되는 부분에 대해서는 Windows 8.1 app에서는 처리가 불가능 할 것 같다. 기본적으로 Windows Phone 8.1의 Play방법과 비슷하기 때문인데...나중에 UWP으로 포팅할 때 고민해야 할 것 같다.




5. Windows Phone 8.1 문제 해결

기본적인 사용 방법은 참고의 링크를 이용하기 바란다. 여기에 간단히 몇줄 써서 설명할 수 있는 내용이 아니다.


* Sample project에서 처럼 PlayList를 관리하는 추가 프로젝트는 필요 없다. 모든 작업을 BackgroundAudioTask를 통해서 하는 것이 훨씬 쉽다. 가득이나 Background와 Foreground간에 데이터도 수시로 주고 받아야하는데..PlayList까지 끼어들면 머리만 복잡해 진다. PlayList를 BackgroundAudioTask에 넣어서 사용하자.


* BackgroundAudioTask는 Foreground App이 Suspended, Terminated가되어도 계속 실행이 된다. 그러니, 그 내부에서 Play, Pause, Next, Previous ... 등의 작업이 단독으로 가능해야한다. Foreground App은 그냥 UI만 살짝 표시해줄 뿐이다.


* SystemMediaTransportControls에 Play정보를 출력하고, 명령 버튼에 대한 처리를 꼭 해야한다. BackgroundAudioTask 단독으로도 실행되기 때문에, 이 컨트롤과 연결 상태를 유지하는 것이 중요하다.


* Wi-Fi에서 Background audio play를하면 0x80072F30 오류가 발생한다. -> 이 문제에 대해서는 해결 방법이 없다. 우선 내가 가지고 있는 루미아 630에서는 Wifi로 접속이 되어있는지 여부를 확인할 수 있는 방법 없다. (몇 몇 예제 코드들을 이용해서 테스트를 해 보았지만 실패했다.) 그렇다고, App에서 네트워크 관련 부분을 변경 할 수 없기 때문에, 오류가 발생하면 발생한대로 메시지를 출력하는 정도가 전부이다.


* Background audio play를 스트리밍으로 1시간 정도 플래이 하면,  The media resource is not supported : HRESULT=0xC00D001A 오류가 발생한다. 사용자가 다시 앱을 시작하고, 명령을 입력해야 다시 동작하는 시나리오로 만들 수 밖에 없다. 아마도 네트워크 자원을 1시간까지만 사용이 가능한 것으로 판단된다. 반대로, 로컬 파일을 플레이하는 경우에는 1시간 이상 플레이해도 문제가 없다.


* Uri 재생하기

BackgroundMediaPlayer.Current.SetUriSource(new Uri(uri));


 

* Local File 재생하기

//_currentPlayStream : 전역 변수로 재생하기 전에 클리어 시켜주도록 한다.

IStorageFile sf = await StorageFile.GetFileFromPathAsync(localFilePath);
_currentPlayStream = await sf.OpenStreamForReadAsync();
if (_currentPlayStream == null) return;
BackgroundMediaPlayer.Current.SetStreamSource(_currentPlayStream.AsRandomAccessStream());


 

* Windows 10 용 백그라운드 오디오 셈플을 다운받아서 실행해 보았지만, 실행이 되지 않았다. 그래서 코드 내용만 확인했는데 Windows Phone 8.1처럼 BackgroundTask를 생성한 후에 작업을 하도록 되어있다. 이 부분은 나중에 실행 가능한 코드가 나오면 다시 확인해 보아야 할 것 같다.


* 마지막 미친 전투력이 필요한 구간이 있다.

앱 시작 -> 플레이 화면 이동 -> 플레이 -> 화면 off or 다른 화면으로 전환 (예상에는 Suspended가 되는 것 같은데...) -> 다시 화면으로 복귀 -> Resume이 될 것이라고 생각했지만..앱이 처음부터 다시 시작된다.(루미아 630 512MB) 설마 장치마다 다른거 아닌가;;

Visual Studio 2013을 이용한 Debug 모드 Device에서 재현이 않된다.(Suspended, Resume은 재현 가능하지만..이때는 앱이 처음부터 시작하지는 않는다), 에뮬레이터로 재현도 마찬가지...

앱을 디바이스에 배포하고, 실행한 경우에만 발생하기 때문에 , Debug mode 없이 Runtime에서 기존 모든 기능이 정상적으로 동작하도록 만들어야 한다.


1) 여기서 중요한 사항! 이미 백그라운드 서비스는 동작 중이다. 그 백그라운드 서비스와 앱이 다시 연결되어야 한다.

-> 백그라운드 서비스와 앱 사이에 통신을 담당하는 SendMessageToForeground, SendMessageToBackground 서비스를 이용할 때 상대방의 상태를 체그하는 경우 주의해야 한다. 상태 체크하기가 너무 어렵다..어디서 어떻게 되었는지 확인이 않되니..쿨럭

2) 앱이 다시 시작되었을 때 플레이하던 화면으로 다시 보내 주는 방법을 생각해야 한다.

-> 셈플 프로젝트에 있는 ApplicationSettingsHelper 녀석을 이용해서, 플레이 되는 곳의 위치, 플레이 중인 노래 정보 등을 저장해 놓았다가 앱이 다시 시작되면 복구를 시켜 준다.



6. Play, Pause, Next, Previous, 직접 선택의 기능만을 가지고 있는 간단 작업이였는데.. 너무나도 먼길을 돌아온 느낌이다. 아직 완벽한 버전이 아니라서 스토어에 등록을 하지는 못하고 있다. 이제 마무리 된 것 같다. 대부분의 기능이 원하는 정도까지 실행이 된다.

이제 스토어에 등록하는 일만 남은 것 같다.

 



반응형

'UWP & Windows App > Expert' 카테고리의 다른 글

Custom Control 만들기 Part2  (0) 2016.03.23
Custom Control 만들기 Part1  (2) 2016.03.22
Using Xaml Toolkit - TreeView control part 1  (0) 2015.04.29
Using the MenuFlyoutPresenter style  (0) 2015.04.28
Solve BindingExpression error  (0) 2015.04.23
댓글