21
Universitas Kristen Petra
4. IMPLEMENTASI SISTEM
Pada bab ini akan dibahas tentang implementasi sistem sesuai dengan
analisa dan desain sistem yang sudah dibuat pada bab sebelumnya.
4.1.
Implementasi Aplikasi yang digunakan
Pembuatan aplikasi dalam skripsi ini membutuhkan beberapa
software
penunjang, sebagai berkikut:
•
Android
Studio dengan versi 3.2.1 ,digunakan sebagai aplikasi
Intergrated Development Enviroment, untuk memudahkan pembuatan
aplikasi.
•
Thorn
merupakan
tool yang yang digunakan sebagai
editor
untuk
bahasa pemograman python.
4.2.
Implementasi Program
Implementasi program adalah tahap penerapan rancangan sistem
berdasarkan desain yang telah dibuat menjadi sebuah aplikasi.
Tabel 4.1
Tabel Relasi Program
Proses
Segmen Program
Source Code start
Segmen Program 4.11
Source Code Menampilkan notifikasi
Segmen Program 4.12
Source Code Mirroring layar smartphone
Segmen Program 4.13
Source Code Connect
ke Raspberry Pi
melalui Micro USB
Segmen Program 4.14
Source Code Connect
ke Raspberry Pi
melalui Wi-fi
Segmen Program 4.15
Source Code Connect ke Aplikasi Android
melalui Wi-fi
Segmen Program 4.16
Source Code Connect ke Aplikasi Android
melalui Micro-USB
4.3.
Implementasi Program Android
4.3.1.
Start
Source code ini dijalankan setelah
user menekan tombol start di aplikasi.
Source code ini terdapat di Segmen Program 4.11.
Source Code start
public int onStartCommand(Intent intent, int flags, int startId) { if (intent == null) { return START_NOT_STICKY; } mReceiverIp = intent.getStringExtra(Common.EXTRA_RECEIVER_IP); mResultCode = intent.getIntExtra(Common.EXTRA_RESULT_CODE, -1); mResultData = intent.getParcelableExtra(Common.EXTRA_RESULT_DATA); Log.d(TAG, "Remove IP: " + mReceiverIp); if (mReceiverIp == null) {
return START_NOT_STICKY; }
//if (mResultCode != Activity.RESULT_OK || mResultData == null) { // Log.e(TAG, "Failed to start service, mResultCode: " + mResultCode + ", mResultData: " + mResultData);
// return START_NOT_STICKY; //} mSelectedWidth = intent.getIntExtra(Common.EXTRA_SCREEN_WIDTH, Common.DEFAULT_SCREEN_WIDTH); mSelectedHeight = intent.getIntExtra(Common.EXTRA_SCREEN_HEIGHT, Common.DEFAULT_SCREEN_HEIGHT); mSelectedDpi = intent.getIntExtra(Common.EXTRA_SCREEN_DPI, Common.DEFAULT_SCREEN_DPI); mSelectedBitrate = intent.getIntExtra(Common.EXTRA_VIDEO_BITRATE, Common.DEFAULT_VIDEO_BITRATE); mSelectedFormat = intent.getStringExtra(Common.EXTRA_VIDEO_FORMAT); if (mSelectedFormat == null) { mSelectedFormat = Common.DEFAULT_VIDEO_MIME_TYPE; } if (mReceiverIp.length() <= 0) {
Log.d(TAG, "Start with listen mode"); if (!createServerSocket()) {
Log.e(TAG, "Failed to create socket to receiver, ip: " + mReceiverIp);
return START_NOT_STICKY; }
} else {
Log.d(TAG, "Start with client mode"); if (!createSocket()) {
Log.e(TAG, "Failed to create socket to receiver, ip: " + mReceiverIp);
return START_NOT_STICKY; }
if (!startScreenCapture()) {
Log.e(TAG, "Failed to start capture screen"); return START_NOT_STICKY;
} }
return START_STICKY; }
23
Universitas Kristen Petra
4.3.2.
Notifikasi
Notifikasi digunakan supaya user tidak perlu membuka aplikasi untuk
menghentikan mirroring. Source code untuk notifikasi dapat dilihat pada Segmen
Program 4.1.2
Source Code menampilkan notifikasi
private void showNotification() {
final Intent notificationIntent = new Intent(Common.ACTION_STOP_CAST);
PendingIntent notificationPendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this); builder.setSmallIcon(R.mipmap.ic_launcher) .setDefaults(Notification.DEFAULT_ALL) .setOnlyAlertOnce(true) .setOngoing(true) .setContentTitle(getString(R.string.app_name)) .setContentText(getString(R.string.casting_screen)) .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.action_stop), notificationPendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NT_ID_CASTING, builder.build()); }
private void dismissNotification() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4.3.3.
Mirroring layar smartphone
Pada saat
user
menekan start, aplikasi android langsung melakukan
mirroring yang akan kemudian dikirimkan ke Raspberry Pi.
Source code
mengenai mirroring layar smartphone terdapat di Segmen Program 4.13
Mirroring layar smartphone
private void prepareVideoEncoder() {
mVideoBufferInfo = new MediaCodec.BufferInfo();
MediaFormat format = MediaFormat.createVideoFormat(mSelectedFormat, mSelectedWidth, mSelectedHeight);
int frameRate = Common.DEFAULT_VIDEO_FPS;
// Set some required properties. The media codec may fail if these aren't defined. format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_BIT_RATE, mSelectedBitrate); format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate); format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate); format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 1 seconds between I-frames
// Create a MediaCodec encoder and configure it. Get a Surface we can use for recording into.
try {
mVideoEncoder =
MediaCodec.createEncoderByType(mSelectedFormat);
mVideoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = mVideoEncoder.createInputSurface(); mVideoEncoder.start();
} catch (IOException e) {
Log.e(TAG, "Failed to initial encoder, e: " + e); releaseEncoders();
} }
private void startRecording() { Log.d(TAG, "startRecording"); prepareVideoEncoder();
// Start the video input. mVirtualDisplay =
mMediaProjection.createVirtualDisplay("Recording Display", mSelectedWidth,
mSelectedHeight, mSelectedDpi, 0 /* flags */, mInputSurface,
null /* callback */, null /* handler */); // Start the encoders
drainEncoder(); }
25
Universitas Kristen Petra
private boolean drainEncoder() {
mDrainHandler.removeCallbacks(mDrainEncoderRunnable); while (true) {
int bufferIndex =
mVideoEncoder.dequeueOutputBuffer(mVideoBufferInfo, 0);
if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { // nothing available yet
break;
} else if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// should happen before receiving buffers, and should only happen once
//if (mTrackIndex >= 0) {
// throw new RuntimeException("format changed twice"); //}
//mTrackIndex =
mMuxer.addTrack(mVideoEncoder.getOutputFormat());
//if (!mMuxerStarted && mTrackIndex >= 0) { // mMuxer.start();
// mMuxerStarted = true; //}
} else if (bufferIndex < 0) {
// not sure what's going on, ignore it } else {
ByteBuffer encodedData = mVideoEncoder.getOutputBuffer(bufferIndex); if (encodedData == null) {
throw new RuntimeException("couldn't fetch buffer at index " + bufferIndex);
}
//Log.d(TAG, "Video buffer offset: " +
mVideoBufferInfo.offset + ", size: " + mVideoBufferInfo.size); if (mVideoBufferInfo.size != 0) { encodedData.position(mVideoBufferInfo.offset); encodedData.limit(mVideoBufferInfo.offset + mVideoBufferInfo.size); if (mSocketOutputStream != null) { System.out.println("Jalan : "+ctr); try {
byte[] b = new byte[encodedData.remaining()]; encodedData.get(b); if (mIvfWriter != null) { mIvfWriter.writeFrame(b, mVideoBufferInfo.presentationTimeUs); } else { mSocketOutputStream.write(b); } } catch (IOException e) {
Log.d(TAG, "Failed to write data to socket, stop casting");
e.printStackTrace(); stopScreenCapture(); return false;
System.out.println("Finish : "+ctr); ctr++; } } mVideoEncoder.releaseOutputBuffer(bufferIndex, false); if ((mVideoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { break; } } } //System.out.println("Casting video"); mDrainHandler.postDelayed(mDrainEncoderRunnable, 10); return true; }
27
Universitas Kristen Petra
4.3.4.
Menghubungkan Aplikasi Android ke Raspberry PI Melalui Micro
USB
Micro USB mode akan menghubungkan aplikasi android ketika
smartphone
terhubung dengan Raspberry Pi melalui kabel
micro USB, dan
menekan tombol start.
Source
code untuk menghubungkan aplikasi android
dengan Raspberry Pi melalui micro USB terdapat di Segmen Program 4.14.
Source Code connect ke Raspberry Pi melalui Micro USB
private boolean createServerSocket() {
Thread th = new Thread(new Runnable() { @Override
public void run() { try {
mServerSocket = new ServerSocket(Common.VIEWER_PORT);
while (!Thread.currentThread().isInterrupted() && !mServerSocket.isClosed()) {
mSocket = mServerSocket.accept(); CommunicationThread commThread = new CommunicationThread(mSocket);
new Thread(commThread).start(); }
} catch (IOException e) {
Log.e(TAG, "Failed to create server socket or server socket error"); e.printStackTrace(); } } }); th.start(); return true; }
class CommunicationThread implements Runnable { private Socket mClientSocket;
public CommunicationThread(Socket clientSocket) { mClientSocket = clientSocket;
}
public void run() {
while (!Thread.currentThread().isInterrupted()) { try {
BufferedReader input = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
String data = input.readLine();
Log.d(TAG, "Got data from socket: " + data); if (data == null || !data.equalsIgnoreCase("mirror")) { mClientSocket.close(); return; } mSocketOutputStream = mClientSocket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(mSocketOutputStream); osw.write(String.format(HTTP_MESSAGE_TEMPLATE, mSelectedWidth, mSelectedHeight)); osw.flush(); mSocketOutputStream.flush(); if
4.3.5.
Menghubungkan Aplikasi Android ke Raspberry Pi Melalui Wi-fi
User menulis IP address atau memilih dari list kemudian menekan tombol
start untuk melakukan
mirroring.
Source
code untuk menghubungkan aplikasi
android dengan Raspberry Pi melalui Wi-fi terdapat di Segmen Program 4.14.
Source Code Connect ke Raspberry Pi melalui Wi-fi
private boolean createSocket() {
Thread th = new Thread(new Runnable() { @Override
public void run() { try {
InetAddress serverAddr = InetAddress.getByName(mReceiverIp);
mSocket = new Socket(serverAddr, Common.VIEWER_PORT); mSocketOutputStream = mSocket.getOutputStream(); OutputStreamWriter osw = new
OutputStreamWriter(mSocketOutputStream); osw.write(String.format(HTTP_MESSAGE_TEMPLATE, mSelectedWidth, mSelectedHeight)); osw.flush(); mSocketOutputStream.flush(); if (mSelectedFormat.equals(MediaFormat.MIMETYPE_VIDEO_AVC)) {
if (mSelectedWidth == 1280 && mSelectedHeight == 720) {
mSocketOutputStream.write(H264_PREDEFINED_HEADER_1280x720);
} else if (mSelectedWidth == 800 && mSelectedHeight == 480) {
mSocketOutputStream.write(H264_PREDEFINED_HEADER_800x480); } else {
Log.e(TAG, "Unknown width: " + mSelectedWidth + ", height: " + mSelectedHeight); mSocketOutputStream.close(); mSocket.close(); mSocket = null; mSocketOutputStream = null; } } else if (mSelectedFormat.equals(MediaFormat.MIMETYPE_VIDEO_VP8)) {
mIvfWriter = new IvfWriter(mSocketOutputStream, mSelectedWidth, mSelectedHeight);
mIvfWriter.writeHeader(); } else {
Log.e(TAG, "Unknown format: " + mSelectedFormat); mSocketOutputStream.close(); mSocket.close(); mSocket = null; mSocketOutputStream = null; } return; } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
29
Universitas Kristen Petra
mSocket = null; mSocketOutputStream = null; } }); th.start(); try { th.join();
if (mSocket != null && mSocketOutputStream != null) { return true; } } catch (InterruptedException e) { e.printStackTrace(); } return false; }
4.4.
Implementasi Program Python di Raspberry Pi
4.4.1.
Menghubungkan Raspberry Pi ke Aplikasi Android Melalui Wi-fi
Raspberry Pi akan menampilkan diri di list dalam aplikasi android dan
ketika user menekan Raspberry Pi yang tampil di list app nya dan menekan start
maka Raspberry Pi akan menerima frame yang dikirim dari aplikasi android.
Source Code Raspberry Pi ke Android Melalui Wi-fi.
Source Code Connect ke Aplikasi Android melalui Wi-fi
#!/usr/bin/env python
from threading import Thread
from subprocess import Popen, PIPE, STDOUT import select, socket
import SocketServer HOST = '' PORT = 53515 IP = '192.168.43.142' bufferSize = 1024 meta_data = '{"port":%d,"name":"PyReceiver @ %s","id":"%s","width":1280,"height":960,"mirror":"h264","audio":" pcm","subtitles":"text/vtt","proxyHeaders":true,"hls":false,"upse ll":true}' % (PORT, IP, IP)
SAVE_TO_FILE = False class MyTCPHandler(SocketServer.BaseRequestHandler): def handle(self): if SAVE_TO_FILE: f = open('video.raw', 'wb') p = Popen(['ffplay', '-framerate', '30', '-'], stdin=PIPE, stdout=PIPE) #p = Popen(['gst-launch-1.0', 'fdsrc', '!', 'h264parse', '!', 'avdec_h264', '!', 'autovideosink'], stdin=PIPE,
stdout=PIPE)
skiped_metadata = False while True:
data = self.request.recv(bufferSize) if data == None or len(data) <= 0: break
if not skiped_metadata:
print "Client connected, addr: ", self.client_address[0]
if data.find('\r\n\r\n') > 0:
last_ctrl = data.find('\r\n\r\n') + 4 print 'Recv control data: ',
data[0:last_ctrl] if len(data) > last_ctrl: p.stdin.write(data[last_ctrl:]) if SAVE_TO_FILE: f.write(data[last_ctrl:]) skiped_metadata = True else:
31
Universitas Kristen Petra
p.stdin.write(data) if SAVE_TO_FILE: f.write(data) p.kill() if SAVE_TO_FILE: f.close() def resp_hello(ip, port):
send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) send_sock.sendto(meta_data, (ip, port))
def handle_discovery(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('', PORT)) s.setblocking(0) while True: result = select.select([s],[],[]) if len(result[0]) <= 0: continue msg, address = result[0][0].recvfrom(bufferSize) print 'Receive broadcast msg: ', msg
if msg == 'hello':
print 'Got discover msg, src ip: %s, port: %d' % (address[0], address[1])
resp_hello(address[0], address[1])
if __name__ == "__main__":
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler) server_thread = Thread(target=server.serve_forever)
server_thread.daemon = True server_thread.start()
handle_discovery() server.shutdown()
4.4.2.
Menghubungkan Raspberry Pi Dengan smartphone Melalui Micro
USB
Source
code untuk
connect
ke aplikasi android melalui
Micro USB.
Source code
untuk menghubungkan Raspberry Pi dengan
smartphone
melalui
micro USB ada pada Segmen Program 4.17.
Source Code Raspberry Pi ke Android Melalui USB
#!/usr/bin/env python
from subprocess import Popen, PIPE, STDOUT import socket
PORT = 53516 bufferSize = 1024 SAVE_TO_FILE = False def connect_to_server():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = ('localhost', PORT)
print 'Connecting to %s port %s' % server_address sock.connect(server_address)
try:
# Send data
message = 'mirror\n'
print 'Sending mirror cmd' sock.sendall(message) if SAVE_TO_FILE: f = open('video_client.raw', 'wb') p = Popen(['ffplay', '-framerate', '30', '-'], stdin=PIPE, stdout=PIPE) #p = Popen(['gst-launch-1.0', 'fdsrc', '!', 'h264parse', '!', 'avdec_h264', '!', 'autovideosink'], stdin=PIPE, stdout=PIPE)
skiped_metadata = False while True:
data = sock.recv(bufferSize)
if data == None or len(data) <= 0: break
if not skiped_metadata:
if data.find('\r\n\r\n') > 0:
last_ctrl = data.find('\r\n\r\n') + 4
print 'Recv control data: ', data[0:last_ctrl] if len(data) > last_ctrl: p.stdin.write(data[last_ctrl:]) if SAVE_TO_FILE: f.write(data[last_ctrl:]) skiped_metadata = True else: p.stdin.write(data) if SAVE_TO_FILE: f.write(data) p.kill()
33
Universitas Kristen Petra
if SAVE_TO_FILE: f.close() finally: sock.close() if __name__ == "__main__": connect_to_server()