libstreaming เป็น API ที่ช่วยให้คุณสามารถใช้โค้ดเพียงไม่กี่บรรทัดเพื่อสตรีมกล้องและ/หรือไมโครโฟนของอุปกรณ์ที่ใช้ระบบ Android โดยใช้ RTP ผ่าน UDP
ขั้นตอนแรกที่คุณจะต้องทำให้สำเร็จเพื่อเริ่มเซสชันการสตรีมให้กับเพียร์บางคนเรียกว่า 'การส่งสัญญาณ' ในระหว่างขั้นตอนนี้ คุณจะติดต่อผู้รับและส่งคำอธิบายของสตรีมที่เข้ามา คุณมีสามวิธีในการทำเช่นนั้นกับ libstreaming
เอกสาร javadoc ฉบับเต็มของ API มีอยู่ที่นี่: http://guigui.us/libstreaming/doc
มีสามวิธีบน Android ในการรับข้อมูลที่เข้ารหัสจากอุปกรณ์ต่อพ่วง:
MediaRecorder API ไม่ได้มีไว้สำหรับแอปพลิเคชันสตรีมมิ่ง แต่สามารถใช้เพื่อดึงข้อมูลที่เข้ารหัสจากอุปกรณ์ต่อพ่วงของโทรศัพท์ เคล็ดลับคือการกำหนดค่าอินสแตนซ์ MediaRecorder ให้เขียนไปยัง LocalSocket แทนที่จะเป็นไฟล์ปกติ (ดู MediaStream.java )
แก้ไข: ตั้งแต่ Android Lollipop โดยใช้ LocalSocket ไม่สามารถทำได้อีกต่อไปด้วยเหตุผลด้านความปลอดภัย แต่การใช้ ParcelFileDescriptor ก็ช่วยได้ รายละเอียดเพิ่มเติมในไฟล์ MediaStream.java ! (ขอบคุณคนเหล่านั้นสำหรับความเข้าใจ)
แฮ็คนี้มีข้อจำกัดบางประการ:
เป็นการยากที่จะบอกว่าแฮ็คนี้จะทำงานได้ดีเพียงใดบนโทรศัพท์ มันทำงานได้ดีบนอุปกรณ์หลายชนิด
MediaCodec API ไม่มีข้อจำกัดที่ฉันเพิ่งกล่าวถึง แต่มีปัญหาในตัวมันเอง จริงๆ แล้ว มีสองวิธีในการใช้ MediaCodec API: โดยใช้บัฟเฟอร์หรือกับพื้นผิว
เมธอด buffer-to-buffer ใช้การเรียก 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() วิธีนี้อาจเป็นวิธีที่ดีที่สุดในการเข้ารหัสวิดีโอ Raw จากกล้อง แต่ต้องใช้ Android 4.3 ขึ้นไป
แพ็คเกจ gl มีไว้สำหรับการใช้ MediaCodec API กับพื้นผิวโดยเฉพาะ
It is not yet enabled by default in libstreaming but you can force it with the setStreamingMethod(byte) method.
Once raw data from the peripherals has been encoded, it is encapsulated in a proper RTP stream. อัลกอริธึมการรับแพ็กเก็ตที่ต้องใช้ขึ้นอยู่กับรูปแบบของข้อมูล (H.264, H.263, AMR และ AAC) และทั้งหมดระบุไว้ใน RFC ที่เกี่ยวข้อง:
If you are looking for a basic implementation of one of the RFC mentionned above, check the sources of corresponding class.
RTCP packets are also sent to the receiver since version 2.0 of libstreaming. Only Sender Reports are implemented. จำเป็นจริงๆ สำหรับการลิปซิงค์
แพ็คเกจ 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 " />
ตัวอย่างนี้ดึงมาจากแอป Android ที่เรียบง่ายนี้ ซึ่งอาจเป็นส่วนหนึ่งของกิจกรรม ชิ้นส่วน หรือบริการ
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 อำนวยความสะดวกในการสร้างอ็อบเจ็กต์ เซสชัน จำเป็นต้องมีการเรียก setSurfaceView สำหรับการสตรีมวิดีโอ ซึ่งไม่ใช่เรื่องน่าแปลกใจเนื่องจาก Android ต้องการพื้นผิวที่ถูกต้องสำหรับการบันทึกวิดีโอ (เป็นข้อจำกัดที่น่ารำคาญของ MediaRecorder API) บน Android 4.3 การสตรีมโดยไม่มี SurfaceView สามารถทำได้แต่ยังไม่ได้นำมาใช้ จำเป็นต้องมีการเรียก setContext(Context) โดยอนุญาตให้อ็อบเจ็กต์ H264Stream และอ็อบเจ็กต์ AACStream จัดเก็บและกู้คืนข้อมูลโดยใช้ SharedPreferences
วัตถุ เซสชัน แสดงถึงเซสชันการสตรีมไปยังเพียร์บางส่วน มันมีออบเจ็กต์ Stream หนึ่งรายการขึ้นไปที่เริ่มต้น (หยุดการตอบสนอง) เมื่อมีการเรียกใช้เมธอด start() (resp. stop() )
เมธอด getSessionDescription() จะส่งคืน SDP ของเซสชันในรูปแบบของ String ก่อนที่จะโทร คุณต้องตรวจสอบให้แน่ใจว่าได้กำหนด ค่าเซสชัน แล้ว หลังจากการเรียก configuration() หรือ startPreview() บนอินสแตนซ์เซสชันของคุณ ระบบจะเรียกการเรียกกลับ onSessionConfigured()
ในตัวอย่างที่นำเสนอข้างต้น อินสแตนซ์ของเซสชันถูกใช้ในลักษณะอะซิงโครนัสและการเรียกไปยังวิธีการของมันจะไม่บล็อก คุณจะรู้ว่าเมื่อสิ่งต่าง ๆ เสร็จสิ้นเมื่อมีการโทรกลับ
คุณยังสามารถใช้วัตถุเซสชันในลักษณะซิงโครนัสได้ดังนี้:
// 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 ();
พอร์ตถูกจัดเก็บเป็น String ในการตั้งค่าอย่างแน่นอน ซึ่งมีเหตุผลที่ดี วัตถุ 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 นี้เพื่อดูว่าสแตกการสตรีมนี้สามารถใช้งานได้อย่างไรและทำงานอย่างไร