The Challenge

I wanted to turn old Android phones into security cameras without relying on cloud services or subscriptions. The idea was simple: install an app, connect to WiFi, and stream video to any browser. The execution? Not so simple.

Tech Stack

  • Kotlin - 100% Kotlin codebase
  • Jetpack Compose - Modern declarative UI
  • Camera2 API - Low-level camera control
  • Ktor - Embedded HTTP server
  • MVVM Architecture - Clean separation of concerns
  • Coroutines & Flow - Asynchronous operations
  • Material Design 3 - Modern UI components

Key Features Implemented

1. Real-time Video Streaming

The core challenge was streaming camera frames efficiently. I implemented MJPEG streaming over HTTP, which works universally in web browsers without plugins.

// Simplified streaming logic
fun startStreaming() {
    cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
        override fun onOpened(camera: CameraDevice) {
            val captureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
            val imageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 2)
            
            imageReader.setOnImageAvailableListener({ reader ->
                val image = reader.acquireLatestImage()
                val buffer = image.planes[0].buffer
                val bytes = ByteArray(buffer.remaining())
                buffer.get(bytes)
                
                // Stream JPEG frame to HTTP clients
                streamFrame(bytes)
                image.close()
            }, handler)
        }
    })
}

2. Motion Detection

Implemented frame differencing algorithm to detect movement and trigger recordings:

  • Convert frames to grayscale
  • Calculate pixel differences between frames
  • Apply threshold to filter noise
  • Trigger recording when motion detected

3. Embedded HTTP Server

Used Ktor to create a lightweight web server running directly on the phone:

  • Serves live video stream
  • Provides web-based control interface
  • Handles API requests for settings
  • No external server needed

Challenges Faced

1. Camera2 API Complexity

The Camera2 API is powerful but notoriously complex. Key challenges:

  • Handling different device capabilities
  • Managing camera sessions and capture requests
  • Proper resource cleanup to prevent crashes
  • Dealing with rotation and orientation changes

2. Performance Optimization

Streaming high-quality video on mobile required careful optimization:

  • Adjusted JPEG compression quality dynamically
  • Implemented frame rate throttling based on network
  • Used coroutines for non-blocking operations
  • Managed memory carefully to prevent leaks

3. Battery Management

Running camera continuously drains battery fast:

  • Implemented wake locks for continuous operation
  • Added foreground service for background running
  • Provided battery optimization warnings
  • Added motion-triggered recording to reduce usage

Architecture

MVVM Pattern Implementation:

// ViewModel manages camera state
class CameraViewModel : ViewModel() {
    private val _streamState = MutableStateFlow(StreamState.Stopped)
    val streamState: StateFlow = _streamState.asStateFlow()
    
    fun startStreaming() {
        viewModelScope.launch {
            cameraRepository.startCamera()
                .collect { result ->
                    _streamState.value = StreamState.Streaming(result)
                }
        }
    }
}

// Compose UI observes state
@Composable
fun CameraScreen(viewModel: CameraViewModel) {
    val streamState by viewModel.streamState.collectAsState()
    
    when (streamState) {
        is StreamState.Streaming -> ShowStreamingUI()
        is StreamState.Stopped -> ShowStartButton()
        is StreamState.Error -> ShowError()
    }
}

Lessons Learned

  1. Start with MVP - Basic streaming first, features later
  2. Test on real devices - Emulator can't test camera properly
  3. Handle edge cases - Network drops, permission denials, low memory
  4. User feedback is gold - Real users found issues I never considered
  5. Documentation matters - Especially for complex APIs like Camera2

Results

  • Successfully published on Play Store
  • Works on devices from Android 7.0+
  • No external dependencies or subscriptions
  • Active user base providing feedback
  • Learned advanced Android development concepts

Want to Build Something Similar?

Building EasyCCTV taught me invaluable lessons about Android camera systems, networking, and performance optimization. If you're working on a similar project or need an experienced Android developer, I'm available for consultation and development work.

Need an Android Developer?

I specialize in Kotlin, Jetpack Compose, and Material Design 3. Check out my portfolio or get in touch to discuss your project.