WPF – XDL Tutorial

XDL VideoView 활용 세번째

(piXoneer XDL Tutorial)

 

 

 

 

 

 

 NXVideoView를 활용하여 동영상 플레이어를 구현해 봅니다.

Timer를 통해 총 Frame수와 현재 도시되는 Frame정보를 획득하여

Slider를 통해 정보를 업데이트하여 도시하게 됩니다.

 

 

 2019. 04.

 

 

목차

XDL VideoView 활용 세번째... 1

1    사용하기... 1

1.1    Play 시간 컨트롤 넣기... 1

1.2    Slider 동기화... 1

 

 

 

 

1     사용하기

본 튜터리얼을 공부하기 전에 먼저 XDL VideoView 활용 두번째를 먼저 선행하시기 바랍니다. XDL VideoView 활용 두번째에 이어서 진행합니다.

1.1    Play 시간 컨트롤 넣기

1.1.1     디자인창에서 labelSlider를 이용해 다음 아래와 그림 같이 디자인한다. 표를 참고하여 “currentTimeLabel”(1)에 그리고 “playControlSlider”(2), “totalTimeLabel”(3)에 입력한다. (자세한 UI 디자인에 대해서는 샘플코드를  참조한다.)

 

 

Number

Control Type

Name

(1)

Label

currentTimeLabel

(2)

Slider

palyControlSlider

(3)

Label

totalTimeLabl

 

 

1.2    Slider 동기화

1.2.1     Window창에서 Menu[File]-[Open]을 더블 클릭하여 함수를 자동 생성하고 두 번째 매뉴얼을 기준으로 다음과 같이 기능을 추가한다. Timer를 추가하고 Frame정보를 위한 TotalFrameCurrentFrame을 설정한다.

 

public partial class MainWindow : Window
{
    enum VideoAction { STOP, PLAYING, PAUSED }
    struct VideoState
    {
       public XVideo video;            // 파일이나 네트워크로부터 입력되는 
// 스트리밍데이터를 제어하는 기능을 수행할 객체 선언
       public XVideoChannel videoChannel;  // 동영상 개체에 포함된 채널 객체 선언
       public string videoFilePath;        // 동영상 파일 경로
       public VideoAction action;          // 비디오 플레이 상태를 정의하는 객체 선언
public long currentFrame;              // 재생중인 동영상의 현재 프레임 위치
       public long totalFrame;                // 동영상 전체 프레임 수  
 

     }
     private XVideoIO m_videoIO = null;
     private VideoState VS;                 // 비디오 상태를 관리하는 객체 선언

private System.Threading.Timer m_timer;    // 재생 컨트롤 바 동작을 위한 타이머
private object m_lockCurFrame = new object();   
// 재생 컨트롤 바와의 동기화를 위한 Lock 객체

 

1.2.2     Timer를 생성하고 TotalFrameCurrentFrame를 초기화 시킨다.

public MainWindow()
{
       InitializeComponent();
       m_videoIO = new XVideoIO();  // VideoIO를 생성

       VideoInit();
       nxVideoLayerOverlay1.LayerVisible = true;

// 재생 컨트롤 바 동작을 위한 타이머 생성
       m_timer = new System.Threading.Timer(timer_Tick);

       // 타이머 옵션 변경
       m_timer.Change(System.Threading.Timeout.Infinite, 
System.Threading.Timeout.Infinite);
}

private void VideoInit()
{
       // VideoState의 초기화
       VS.video = null;
       VS.videoChannel = null;
       VS.videoFilePath = string.Empty;
VS.totalFrame = 0;
       VS.currentFrame = 0;

       VS.action = VideoAction.STOP;
}

 

1.2.3     OnOpen 함수에 TotalFrame을 얻어 VideoState에 저장한다.

// 동영상 파일 열기
public void OnOpen()
{
    try
   {
    string strError = null;
    // 동영상 스트림 정보 가져오기
    VS.video = m_videoIO.OpenFile(VS.videoFilePath, "XFFMPDRIVER", out strError);

        if (VS.video == null)
        {
           MessageBox.Show(this, "동영상 재생에 실패하셨습니다. 파일을 확인해주십시오.", "파일 열기");
           return;
         }

    // 다중 채널을 가진 동영상 객체의 Channel 인덱스
    int nIdxChannel = 0;

    // 동영상 뷰에 재생할 동영상 채널의 설정
    nxVideoView1.SetVideoChannel(VS.video, nIdxChannel);

    // 입력 인덱스에 해당하는 Channel 가져오기
    VS.videoChannel = VS.video.GetChannel(nIdxChannel);

    // GetChannel에 실패할 경우 Null 객체가 return되며, 그에 대한 예외처리
    if (VS.videoChannel == null)
    {
           MessageBox.Show(this, "동영상 재생에 실패하였습니다. 파일을 확인해 주십시오.", "파일 열기");
           return;
     }

      // 동영상 객체에 포함된 Channel 객체 중 해당 Channel 객체를 활성화
      //활성화된 객체만 스트리밍이 수행
      VS.videoChannel.Activate();

      // 동영상의 Fram 정보를 얻어오기
      VS.totalFram = VS.videoChannel.GetNumFramesVideo();

}

     catch (Exception ex)
     {
        Console.WriteLine(ex);
        MessageBox.Show(this, "재생 실패!", "동영상");
        VS.action = VideoAction.STOP;
     }
}

 

1.2.4     OnStop 함수에 에 currentFrame을 초기화 시킨다.

public void OnStop()
{
    if (VS.videoChannel != null)
    {
        // 재생 Frame Buffer를 삭제
        VS.videoChannel.ClearFrameBuffer();
        // 동영상 재생 스크린을 갱신한다.
        nxVideoView1.RefreshScreen();
        // 동영상 재생 중지
        VS.videoChannel.Stop();
        VS.videoChannel = null;
    }

    // 동영상 채널 정보 초기화
    nxVideoView1.ResetVideoChannel();

    if (VS.video != null)
    {
        // 동영상 객체 Close
        VS.video.Close();
        VS.video = null;
    }

VS.currentFrame = 0;
    VS.action = VideoAction.STOP;
}

 

1.2.5     [File]-[Open] 버튼을 눌렀을 때 기능을 업데이트 시킨다.

private void openFileMenuItem_Click(object sender, RoutedEventArgs e)
{
      // 새로운 파일 Open을 수행한다.
      OpenFileDialog openFileDialog = new OpenFileDialog();
      openFileDialog.Filter = "TS file(*ts)|*.ts||";
      openFileDialog.RestoreDirectory = true;

Nullable<bool> result = openFileDialog.ShowDialog();
      if (result != true) return;

      string videoPath = openFileDialog.FileName;

      // 동영상 파일의 존재 유무 체크
      if (System.IO.File.Exists(videoPath) == false)
      {
          MessageBox.Show(this, "해당 경로에 영상이 존재하지 않습니다.", "오류");
          return;
       }

// 파일 경로를 저장한다.
     VS.videoFilePath = videoPath;

     // 둥영상의 재생상태가 중지가 아닐 경우 동영상 재생 중지
     OnStop();
 
// 동영상 상태를 업데이트 시키기 위해 Timer를 작동 시킨다. 100msec간격으로 설정한다.
     OnTimer();

// Play되는 시간 정보를 도시하기 위해 Timer설정과 Slider와 Label을 초기화 한다. 
     InitSliderLabel();


     // 동영상 스트리밍 환경을 생성시킨다.
     OnOpen();
 
     // 동영상을 Play 시킨다.
     OnPlay();
}

 

1.2.6     [], [], [] 버튼을 눌렀을 때 기능을 업데이트 시킨다.

private void playButton_Click(object sender, RoutedEventArgs e)
{
    // 비디오가 Stop인 상태인 경우 다시 비디오를 저장된 파일 경로로부터 Open해서 설정한다. 
    if (VS.action == VideoAction.STOP)
    {
        // 동영상 상태를 업데이트 시키기 위해 Timer를 작동 시킨다. 
        OnTimer();

        // 동영상 스트리밍 환경을 생성시킨다.
        OnOpen();
    }

    // 설정된 비디오를 Play한다.
    OnPlay();
}

private void pauseButton_Click(object sender, RoutedEventArgs e)
{
{
    // Play되고 있는 비디오를 Pause시킨다.
    OnPause();
}

private void stopButton_Click(object sender, RoutedEventArgs e)
{
    // Play가 종료되었으므로 Timer를 종료 시킨다.
    StopTimer();

    // Play를 중단한다.
    OnStop();

    // Play가 Stop되었으므로 Slider컨트롤과 Label을 초기화 시킨다.
    InitSliderLabel();
}

 

1.2.7     InitSliderLabel, OnTimer, StopTimer를 정의한다.

private void InitSliderLabel()
{
      // Slider를 설정하기 위해 컨트롤의 최대값을 설정한다. 
      playControlSlider.Maximum = unchecked((int)VS.totalFrame);

      // Total 시간에 대해 정보를 Label로 설정한다. 
      int nTotalSec = unchecked((int)(VS.totalFrame * (1.0 / 30)));
      TimeSpan getTotalTimeSpan = TimeSpan.FromSeconds(nTotalSec);
      string strTime = getTotalTimeSpan.ToString("hh':'mm':'ss");
      totalTimeLabel.Content = strTime;
}

public void OnTimer()
{
    m_timer.Change(0, 100);
}

public void StopTimer()
{
    if (m_timer != null)
    {
        m_timer.Change(System.Threading.Timeout.Infinite, 
System.Threading.Timeout.Infinite);
    }
}

 

1.2.8     Window 창에서 Video Overlay Layer컨트롤을 클릭하고 [속성]-[이벤트 ] 탭에서 OnOrthoRender행을 더블 클릭하여 nxVideoLayerOverlay1_OnOrthoRender함수를 자동 생성한다. NXVideoDrawArgs 인자로 넘어오는 값으로부터 PTS값을 얻어 현재 Frame이 몇 번째인지를 계산하여 currentFrame에 저장하도록 한다. 참고로 OnOrthoRender는 화면이 갱신될 때 마다 호출되는 Callback함수이다.

// PTS 값을 얻기 위해 OnOrthoRender 함수를 이용한다.
private bool nxVideoLayerOverlay1_OnOrthoRender(NXVideoLayer sender, NXVideoDrawArgs DrawArgs)
{

     try
     {
         if (VS.videoChannel == null) return false;

         lock (m_lockCurFrame)
         {
             // 현재 재생중인 화면이 PTS(Presentation TimeStamp) 값 얻어오기
             Int64 pts = DrawArgs.PTS;

             // PTS를 이용한 현재 재생중인 화면의 프레임 위치를 얻어오기
             VS.currentFrame = VS.videoChannel.PtsToFrameNumber(pts);
             if (VS.currentFrame >= VS.totalFrame) VS.currentFrame =
 VS.totalFrame;
             if (VS.currentFrame < 0) VS.currentFrame = 0;
          }
       }

         catch (System.Exception ex)
         {
            Console.WriteLine(ex);
         }
       return default(bool);
}

 

1.2.9     Timer함수를 통해 현재 도시되는 Frame이 어느 정도 되는 지와 각종 재생 상태를 업데이트 시키고 만약 Play가 종료시점에 도달하면 Stop을 수행한다.

private void timer_Tick(object state)
{
    lock (m_lockCurFrame)
    {
          if (VS.action != VideoAction.STOP)
          {
               // 현재 재생되고 있는 동영상의 시간을 계산
               int nSec = unchecked((int)(VS.currentFrame * (1.0 / 30)));
               int nTotalSec = unchecked((int)(VS.totalFrame * (1.0 / 30)));

               long nCheckFrame = VS.totalFrame - VS.currentFrame;

               if (nCheckFrame <= 30)
               {
                   nSec = nTotalSec;
               }

               string strTime;

               TimeSpan getTimeSpan = TimeSpan.FromSeconds(nSec);
               strTime = getTimeSpan.ToString("hh':'mm':'ss");
               int nCurFrame = unchecked((int)VS.currentFrame);

               Dispatcher.BeginInvoke(new Action(delegate
               {
                    // 현재 재생되고 있는 동영상의 시간을 문자형태로 화면 도시
                    currentTimeLabel.Content = strTime;

                    // 현재 재생되고 있는 동영상의 시간을 Track 바 컨트롤에 도시
                    playControlSlider.Value = nCurFrame;
                    if (nSec == nTotalSec)
                    {
                        playControlSlider.Value = playControlSlider.Maximum;

                        // Play가 종료되었으므로 Timer를 종료 시킨다.
                        StopTimer();

                        // Play를 중단한다.
                        OnStop();

                        // Play가 Stop되었으므로 Slider컨트롤과 Label을 초기화 시킨다.
                        InitSliderLabel();
                 }
             }));         
        }
    }
}

 

1.2.10  [F5]키를 눌러 프로그램을 실행하여 홈페이지에서 다운 받은 stream.ts파일을 Open한다.