libstreaming 은 단 몇 줄의 코드만으로 UDP를 통한 RTP를 사용하여 Android 기반 장치의 카메라 및/또는 마이크를 스트리밍할 수 있는 API입니다.
일부 피어에 대한 스트리밍 세션을 시작하기 위해 달성해야 하는 첫 번째 단계를 '신호 보내기'라고 합니다. 이 단계에서는 수신자에게 연락하여 들어오는 스트림에 대한 설명을 보냅니다. libstreaming을 사용하면 이를 수행하는 세 가지 방법이 있습니다.
API의 전체 javadoc 문서는 여기에서 볼 수 있습니다: http://guigui.us/libstreaming/doc
Android에는 주변 장치에서 인코딩된 데이터를 가져오는 세 가지 방법이 있습니다.
MediaRecorder API는 스트리밍 애플리케이션용이 아니지만 휴대폰 주변 장치에서 인코딩된 데이터를 검색하는 데 사용할 수 있습니다. 비결은 일반 파일 대신 LocalSocket 에 쓰도록 MediaRecorder 인스턴스를 구성하는 것입니다( MediaStream.java 참조).
편집: Android Lollipop부터는 보안상의 이유로 LocalSocket 을 사용하는 것이 더 이상 불가능합니다. 그러나 ParcelFileDescriptor 를 사용하면 트릭이 수행됩니다. 자세한 내용은 MediaStream.java 파일을 참조하세요! (인사이트를 주신 분들께 감사드립니다)
이 해킹에는 몇 가지 제한 사항이 있습니다.
이 해킹이 휴대폰에서 얼마나 잘 작동할지 말하기는 어렵습니다. 하지만 많은 장치에서 잘 작동합니다.
MediaCodec API에는 방금 언급한 제한 사항이 없지만 고유한 문제가 있습니다. 실제로 MediaCodec API를 사용하는 방법에는 버퍼를 사용하거나 표면을 사용하는 두 가지 방법이 있습니다.
버퍼 간 방법은 dequeueInputBuffer 및 [ queueInputBuffer ](http://developer.android.com/reference/android/media/MediaCodec.html#queueInputBuffer(int, int, int, long, int))에 대한 호출을 사용하여 인코더에 원시 데이터를 공급합니다. 그거 쉬운 것 같죠? 그렇지 않습니다. 왜냐하면 이 API를 통해 액세스할 수 있는 비디오 인코더는 다양한 색상 형식을 사용하고 있으며 이를 모두 지원해야 하기 때문입니다. 해당 색상 형식 목록은 여기에서 확인할 수 있습니다. 더욱이 많은 인코더는 실제로 제대로 지원하지 않거나 약간의 결함이 있을 수 있는 색상 형식을 지원한다고 주장합니다.
모든 hw 패키지는 이러한 문제를 해결하는 데 전념합니다. 특히 EncoderDebugger 클래스를 참조하세요.
해당 API를 사용한 스트리밍이 실패하면 libstreaming은 MediaRecorder API를 사용한 스트리밍을 대체합니다.
표면-버퍼 메서드는 createInputSurface() 메서드를 사용합니다. 이 방법은 아마도 카메라의 원시 비디오를 인코딩하는 가장 좋은 방법이지만 Android 4.3 이상이 필요합니다.
gl 패키지는 표면과 함께 MediaCodec API를 사용하는 데 전념합니다.
libstreaming에서는 아직 기본적으로 활성화되어 있지 않지만 setStreamingMethod(byte) 메서드를 사용하여 강제로 활성화할 수 있습니다.
주변 장치의 원시 데이터가 인코딩되면 적절한 RTP 스트림에 캡슐화됩니다. 사용해야 하는 패킷화 알고리즘은 데이터 형식(H.264, H.263, AMR 및 AAC)에 따라 다르며 모두 해당 RFC에 지정되어 있습니다.
위에서 언급한 RFC 중 하나의 기본 구현을 찾고 있다면 해당 클래스의 소스를 확인하세요.
libstreaming 버전 2.0부터는 RTCP 패킷도 수신자에게 전송됩니다. 보낸 사람 보고서만 구현됩니다. 실제로 립싱크에 필요합니다.
rtp 패키지는 RTP 패킷에서 인코딩된 데이터의 패킷화를 처리합니다.
< uses-permission android : name = " android.permission.INTERNET " />
< uses-permission android : name = " android.permission.WRITE_EXTERNAL_STORAGE " />
< uses-permission android : name = " android.permission.RECORD_AUDIO " />
< uses-permission android : name = " android.permission.CAMERA " />
이 예제는 이 간단한 안드로이드 앱에서 추출되었습니다. 이는 활동, 조각 또는 서비스의 일부일 수 있습니다.
protected void onCreate ( Bundle savedInstanceState ) {
...
mSession = SessionBuilder . getInstance ()
. setCallback ( this )
. setSurfaceView ( mSurfaceView )
. setPreviewOrientation ( 90 )
. setContext ( getApplicationContext ())
. setAudioEncoder ( SessionBuilder . AUDIO_NONE )
. setAudioQuality ( new AudioQuality ( 16000 , 32000 ))
. setVideoEncoder ( SessionBuilder . VIDEO_H264 )
. setVideoQuality ( new VideoQuality ( 320 , 240 , 20 , 500000 ))
. build ();
mSurfaceView . getHolder (). addCallback ( this );
...
}
public void onPreviewStarted () {
Log . d ( TAG , "Preview started." );
}
@ Override
public void onSessionConfigured () {
Log . d ( TAG , "Preview configured." );
// Once the stream is configured, you can get a SDP formated session description
// that you can send to the receiver of the stream.
// For example, to receive the stream in VLC, store the session description in a .sdp file
// and open it with VLC while streming.
Log . d ( TAG , mSession . getSessionDescription ());
mSession . start ();
}
@ Override
public void onSessionStarted () {
Log . d ( TAG , "Streaming session started." );
...
}
@ Override
public void onSessionStopped () {
Log . d ( TAG , "Streaming session stopped." );
...
}
@ Override
public void onBitrateUpdate ( long bitrate ) {
// Informs you of the bandwidth consumption of the streams
Log . d ( TAG , "Bitrate: " + bitrate );
}
@ Override
public void onSessionError ( int message , int streamType , Exception e ) {
// Might happen if the streaming at the requested resolution is not supported
// or if the preview surface is not ready...
// Check the Session class for a list of the possible errors.
Log . e ( TAG , "An error occured" , e );
}
@ Override
public void surfaceChanged ( SurfaceHolder holder , int format , int width ,
int height ) {
}
@ Override
public void surfaceCreated ( SurfaceHolder holder ) {
// Starts the preview of the Camera
mSession . startPreview ();
}
@ Override
public void surfaceDestroyed ( SurfaceHolder holder ) {
// Stops the streaming session
mSession . stop ();
}
SessionBuilder는 단순히 Session 개체 생성을 용이하게 합니다. setSurfaceView 에 대한 호출은 비디오 스트리밍에 필요합니다. Android에서는 비디오 녹화를 위한 유효한 표면이 필요하므로 이는 놀라운 일이 아닙니다( MediaRecorder API의 성가신 제한 사항입니다). Android 4.3에서는 SurfaceView 없이 스트리밍이 가능하지만 아직 구현되지 않았습니다. setContext(Context) 에 대한 호출이 필요합니다. 이를 통해 H264Stream 객체와 AACStream 객체가 SharedPreferences를 사용하여 데이터를 저장하고 복구할 수 있습니다.
Session 객체는 일부 피어에 대한 스트리밍 세션을 나타냅니다. 여기에는 start() (각각 stop() ) 메서드가 호출될 때 시작(각각 중지됨)되는 하나 이상의 Stream 객체가 포함되어 있습니다.
getSessionDescription() 메소드는 세션의 SDP를 문자열 형식으로 반환합니다. 호출하기 전에 세션이 구성되었는지 확인해야 합니다. Session 인스턴스에서 configure() 또는 startPreview()를 호출한 후 onSessionConfigured() 콜백이 호출됩니다.
위에 제시된 예제에서 Session 인스턴스는 비동기 방식으로 사용되며 해당 메서드에 대한 호출이 차단되지 않습니다. 콜백이 호출되면 작업이 언제 완료되는지 알 수 있습니다.
다음과 같이 동기식으로 Session 객체를 사용할 수도 있습니다.
// Blocks until the all streams are configured
try {
mSession . syncConfigure ();
} catch ( Exception e ) {
...
}
Strinf sdp = mSession . getSessionDescription ();
...
// Blocks until streaming actually starts.
try {
mSession . syncStart ();
} catch ( Exception e ) {
...
}
...
mSession . syncStop ();
위키의 이 페이지와 예제 3을 확인하세요.
< service android : name = " net.majorkernelpanic.streaming.rtsp.RtspServer " />
RtspServer를 재정의하기로 결정한 경우 위의 줄을 그에 따라 변경하십시오.
Editor editor = PreferenceManager . getDefaultSharedPreferences ( this ). edit ();
editor . putString ( RtspServer . KEY_PORT , String . valueOf ( 1234 ));
editor . commit ();
포트는 실제로 환경 설정에 문자열로 저장되어 있는데, 거기에는 그럴 만한 이유가 있습니다. EditTextPreference 객체는 입력을 문자열로 저장하며 이를 정수로 저장하도록 쉽게 구성할 수 없습니다(재정의해야 함).
SessionBuilder . getInstance ()
. setSurfaceHolder ( mSurfaceView . getHolder ())
. setContext ( getApplicationContext ())
. setAudioEncoder ( SessionBuilder . AUDIO_AAC )
. setVideoEncoder ( SessionBuilder . VIDEO_H264 );
// Starts the RTSP server
context . startService ( new Intent ( this , RtspServer . class ));
// Stops the RTSP server
context . stopService ( new Intent ( this , RtspServer . class ));
이 스트리밍 스택의 사용 방법과 성능을 확인하려면 이 github 페이지를 방문하세요.