비디오 파일을 제어하기 위해서는 몇가지 속성을 알아야한다. 이러한 속성은 대부분 NetStream()클래스를 통해서 얻게 된다.
우선 전체재생시간은 NetStream이 onMetaData() 콜벡호출시 duration이라는 값으로 알게 된다.
그리고 현재 재생시간을 알기 위해서는 NetStream의 time속성을 통해 현재 재생시간을 알 수 있다.
최초 재생할 파일을 선택하고 재생하는 것은 NetStream의 play()에서 하지만 이 후 정지와 다시 재생은 NetStream의 pause()와 resume()메서드를 통해 간단하게 구현할 수 있다.
게다가 NetStream에는 seek()라는 메서드를 제공하는 재생할 시간을 입력하면 그 시간과 가장 가까운 비디오 프레임을 재생하게 된다.
* seek시 가까운 프레임 헤더란?
비디오 파일은 시간 압축되어 있는 파일이다. 즉 모든 영상정보를 저장하는것이 아니고 특정 구간마다 거의 움직이지 않는 장면을 한개 저장하고 있다가 조금씩 움직이는 부분의 영상정보를 저장하여 보여준다. 즉 플래시 무비를 만들 때 배경은 1개의 프레임으로 1개의 레이어를 소비하지만 다른 움직이는 것을 여러개의 프레임을 가지고 있다. 이처럼 배경이나 큰 움직임이 있을때만 비디오 프레임이 갱신되고 이후에는 앞서 갱신된 프레임만 보여주기 때문에 정확한 seek위치가 나오질 않는다. 가령 비디오가 시작시 배경만 갱신되고 이후 특정부위만 움직일때 다시 갱신이 없다면 seek하면 제일 처음부터 재생된다는 것이다.
비디오의 사운드를 제어하기 위해서는 NetStream클래스의 속성인 soundTransform에 제어된 soundTransform를 대입한다. 이방법은 SoundChannel과 같은 방법으로 사용한다.
또한 사용자 네트워크가 느린 경우를 생각하여 NetStream에는 bufferTime이라는 속성이 있다. 이는 최초 재생시 또는 중간에 끊김 이후 재생시 설정한 시간동안 데이터를 받고 해당시간이 되면 재생하도록 하는 버퍼타임을 설정한다.
위에서 언급한 속성과 메서드만 있으면 재생/정지 , 프로그래시브바 , skip등 많은 일들을 할 수 있고 이 모든 것은 NetStream()을 통해서 이루어 진다. 아마 어도비에서도 각 클래스를 분담해서 작업했겠지만 사운드도 이처럼 조금 더 제어 클래스에 집중화 되었더라면 하는 아쉬움이 남는다...
위 무비는 재생이 끝나면 Video의 clear()메서드를 통해 화면을 지웠다. 따라서 아래의 무비처럼 재생 종료 후 영상이 남지 않는다. clear()는 removeChild()와 달리 비디오의 현재 재생화면만 지울 뿐 디스플레이 리스트에서 지워지는 것이 아니기 때문에 언제라도 play()가 이루어 지면 화면을 보여주게 된다. 시시콜콜한 소린 왜 했는가면 책(오라일리)에서 이 둘을 같은 개념으로 설명했기에 차이점을 짚어보고 싶었다...
우선 전체재생시간은 NetStream이 onMetaData() 콜벡호출시 duration이라는 값으로 알게 된다.
그리고 현재 재생시간을 알기 위해서는 NetStream의 time속성을 통해 현재 재생시간을 알 수 있다.
최초 재생할 파일을 선택하고 재생하는 것은 NetStream의 play()에서 하지만 이 후 정지와 다시 재생은 NetStream의 pause()와 resume()메서드를 통해 간단하게 구현할 수 있다.
게다가 NetStream에는 seek()라는 메서드를 제공하는 재생할 시간을 입력하면 그 시간과 가장 가까운 비디오 프레임을 재생하게 된다.
* seek시 가까운 프레임 헤더란?
비디오 파일은 시간 압축되어 있는 파일이다. 즉 모든 영상정보를 저장하는것이 아니고 특정 구간마다 거의 움직이지 않는 장면을 한개 저장하고 있다가 조금씩 움직이는 부분의 영상정보를 저장하여 보여준다. 즉 플래시 무비를 만들 때 배경은 1개의 프레임으로 1개의 레이어를 소비하지만 다른 움직이는 것을 여러개의 프레임을 가지고 있다. 이처럼 배경이나 큰 움직임이 있을때만 비디오 프레임이 갱신되고 이후에는 앞서 갱신된 프레임만 보여주기 때문에 정확한 seek위치가 나오질 않는다. 가령 비디오가 시작시 배경만 갱신되고 이후 특정부위만 움직일때 다시 갱신이 없다면 seek하면 제일 처음부터 재생된다는 것이다.
비디오의 사운드를 제어하기 위해서는 NetStream클래스의 속성인 soundTransform에 제어된 soundTransform를 대입한다. 이방법은 SoundChannel과 같은 방법으로 사용한다.
또한 사용자 네트워크가 느린 경우를 생각하여 NetStream에는 bufferTime이라는 속성이 있다. 이는 최초 재생시 또는 중간에 끊김 이후 재생시 설정한 시간동안 데이터를 받고 해당시간이 되면 재생하도록 하는 버퍼타임을 설정한다.
위에서 언급한 속성과 메서드만 있으면 재생/정지 , 프로그래시브바 , skip등 많은 일들을 할 수 있고 이 모든 것은 NetStream()을 통해서 이루어 진다. 아마 어도비에서도 각 클래스를 분담해서 작업했겠지만 사운드도 이처럼 조금 더 제어 클래스에 집중화 되었더라면 하는 아쉬움이 남는다...
위 무비는 재생이 끝나면 Video의 clear()메서드를 통해 화면을 지웠다. 따라서 아래의 무비처럼 재생 종료 후 영상이 남지 않는다. clear()는 removeChild()와 달리 비디오의 현재 재생화면만 지울 뿐 디스플레이 리스트에서 지워지는 것이 아니기 때문에 언제라도 play()가 이루어 지면 화면을 보여주게 된다. 시시콜콜한 소린 왜 했는가면 책(오라일리)에서 이 둘을 같은 개념으로 설명했기에 차이점을 짚어보고 싶었다...
댓글을 달아 주세요
도움을 좀 요청 드려도 될까요?
2009.05.27 15:01 신고 [ ADDR : EDIT/ DEL : REPLY ]현재 flvPlayer를 만들고 있는데.. 다른건 다 됐는데..
저 재생 슬라이더 처리를 못하고 있습니다. 현재 onMetaData 와 onCuePoint를 적용은 했는데..
핸들처리가 안되는 듯한..
ㅋ.. 질문이 이해가 가시나요? ㅠ.ㅠ
package
2009.05.28 00:24 신고 [ ADDR : EDIT/ DEL ]{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.geom.Rectangle;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.ObjectEncoding;
[SWF(width="550" , height="400")]
public class Video_seek_And_NetStatus extends Sprite
{
private var netc:NetConnection;
private var nets:NetStream;
private var video:Video;
private var thumb:Sprite;
private var track:Sprite;
private var progress:Sprite;
private var buffer:Sprite;
private var jump:Sprite;
private var cur:Number;
private var duration:Number;
private var isDown:Boolean;
private var meta:Object = new Object();
public function Video_seek_And_NetStatus()
{
super();
meta.onBWDone = onBWDone;
meta.onMetaData = onMeta;
meta.onCuePoint = onCue;
NetConnection.defaultObjectEncoding= ObjectEncoding.DEFAULT;
netc = new NetConnection();
netc.client = meta;
netc.addEventListener( NetStatusEvent.NET_STATUS, netStatusHandler )
netc.connect( "rtmp://211.48.55.233/upload/" );
}
private function netStatusHandler( e:NetStatusEvent ):void
{
switch (e.info.code) {
case "NetConnection.Connect.Success":
trace("Success")
nets = new NetStream( netc );
nets.client = meta;
nets.addEventListener(NetStatusEvent.NET_STATUS , onStatus );
video = new Video(500 , 375);
video.opaqueBackground = true;
video.attachNetStream( nets );
addChild( video );
nets.play("sample1");
seekPoint();
break;
case "NetStream.Play.StreamNotFound":
trace("Stream not found");
break;
}
}
private function seekPoint( ):void
{
progress = new Sprite();
track = new Sprite();
track.graphics.lineStyle(1);
track.graphics.drawRect( 10, video.height +10 - 2.5 , 450 , 5 );
track.graphics.endFill();
thumb = new Sprite();
thumb.graphics.lineStyle(1);
thumb.graphics.beginFill( 0xFFFFFF );
thumb.graphics.drawRect( 5 , video.height +10 -5 , 10 , 10 );
thumb.graphics.endFill();
buffer = new Sprite();
jump = new Sprite();
jump.graphics.beginFill( 0 , 0 );
jump.graphics.drawRect( 10, video.height +10 - 2.5 , 450 , 5 );
jump.graphics.endFill();
addChild( progress );
addChild( track );
addChild( buffer );
addChild( jump );
addChild( thumb );
addEventListener(Event.ENTER_FRAME , onEnter );
thumb.addEventListener(MouseEvent.MOUSE_DOWN , onDown );
jump.addEventListener( MouseEvent.MOUSE_DOWN , onJump );
stage.addEventListener(MouseEvent.MOUSE_UP , onUp );
}
private function onEnter( e:Event ):void
{
if( duration > 0 ){
if( isDown ){
nets.seek( (duration * thumb.x) / track.width );
}else{
thumb.x = (nets.time / duration) * track.width;
}
//빨간색으로 재생중인 구간을 보여줌
progress.graphics.clear();
progress.graphics.beginFill( 0xFF0000 );
progress.graphics.drawRect( 10, video.height +10 - 2.5 , track.x + thumb.x , 5 );
progress.graphics.endFill();
}
}
private function onJump( e:MouseEvent ):void
{
isDown = true;
nets.pause();
thumb.x = mouseX - 5 - 2.5;
nets.seek( (duration * thumb.x) / track.width );
}
private function onDown( e:MouseEvent ):void
{
isDown = true;
var rect:Rectangle = new Rectangle( 0 , 0 , track.width , 0 );
thumb.startDrag( false , rect );
nets.pause();
}
private function onUp( e:MouseEvent ):void
{
isDown = false;
thumb.stopDrag();
nets.resume();
}
private var statusCode:String;
private function onStatus( e:NetStatusEvent ):void
{
statusCode = e.info.code;
trace("************* onStatus *************")
for( var data:String in e.info ){
trace( data , " : " , e.info[data] )
}
trace("************* End *************")
//비디오가 끝나는 시점
if( e.info.code == "NetStream.Play.Stop" ){
video.clear();
}
}
private function onMeta( data:Object ):void
{
trace("onMeta" , data.duration);
duration = data.duration;
}
private function onCue( data:Object ):void
{
trace("onCue");
}
private function onBWDone( ):void
{
trace("onBWDone");
}
}
}
제가 사용한 코드입니다.
보시면 seekPoint()에서 트랙을 그리고
onEnter()에서 클릭시 nets.seek()라는 녀석이 있습니다. 매개변수는 전체 재생시간과 트랙에서의 핸들러 위치를 용해 구한 값을 설정합니다...클릭이 아닐시에는 그냥 현재 재생되는 시간에 맞게 핸들러의 x를 이동시킵니다...
여기저기 함수가 있어서 통채로 올렸습니다..
원하시는 답이 되었는지 모르겠네요...;;
흠.. 일단 전 타이머를 이용해서 해결을 하긴 했는데..
2009.05.28 10:33 신고 [ ADDR : EDIT/ DEL : REPLY ]좀 아니다 싶네요..
그리고 이것과 비슷한 소스를 보긴 했는데..
생각처럼 적용이 안되더라고요..
알려주신 소스로 다시 한번 공부 좀 해 보겠습니다.
친절한 답변 감사드립니다. ^^
안녕하세요.. 다시 찾아 왔습니다. ^^
2009.06.10 17:58 신고 [ ADDR : EDIT/ DEL : REPLY ]질문이 좀 있어서요..
addChild( track );
요부분부터 진도를 못나가고 있습니다.
track가 어떻게 쓰인건지 설명 좀 부탁 드려요 될까요?
어찌 어찌 적용은 하고 있는데..
저 부분에서
TypeError: Error #1034: 유형 강제 변환에 실패했습니다. flash.display::Sprite@4c95281을(를) mx.core.IUIComponent(으)로 변환할 수 없습니다.
이런 에러 메시지가.. ㅜ.ㅜ
플렉스 MXML에 코딩하시나 보군요~ 뭐 위의 코드는 플래시클래스이구요~ track은 단순히 재생바입니다... 바의 길이는 디자인상 고정이고.. 핸들러의 이동속도가 전체 재생시간과 바의 길이에 비례하여 이동속도를 구하여야 하구요...에러나는 코드는 플렉스UI컴포넌트를 Sprite로 사용하려고 하여 발생한것 같습니다...
2009.06.11 09:27 신고 [ ADDR : EDIT/ DEL ]아~ 이 소스는 플래시였군요... 그래서..
2009.06.11 10:06 신고 [ ADDR : EDIT/ DEL : REPLY ]도움 감사드립니다. ^^
에구.. 언제 완벽히 끝낼 수 있을런지.. ^^