Skip to content

Commit

Permalink
Improve documentation and minor clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
dburckh committed Apr 16, 2024
1 parent 3c6ba6a commit 9a0fea7
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 174 deletions.
250 changes: 169 additions & 81 deletions app/src/main/java/com/homesoft/muxer/CameraViewModel.java

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion app/src/main/java/com/homesoft/muxer/FragmentServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

import kotlin.NotImplementedError;

/**
* Mp4 Fragment Server
* Holds the stream header(ftyp+moov) and the latest movie fragment (moof)
*/
public class FragmentServer implements GatheringByteChannel {
private static final String TAG = FragmentServer.class.getSimpleName();

Expand All @@ -22,6 +26,14 @@ public long write(ByteBuffer[] srcs, int offset, int length) {
throw new NotImplementedError();
}

/**
* This is a little hacky, the FragmentedMp4Writer always writes data in chunks
* The first chuck is the ftyp+moov and all subsequent chunks are moof.
* @param srcs
* The buffers from which bytes are to be retrieved
*
* @return the number of bytes written
*/
@Override
public long write(ByteBuffer[] srcs) throws ClosedChannelException {
if (!isOpen()) {
Expand Down Expand Up @@ -72,7 +84,7 @@ public synchronized void close() {
}

/**
* Return the common MP4 header (FTYP + MOOV)
* Return the common MP4 header (ftyp + moov)
*/
public synchronized ByteBuffer getHeader() throws ClosedChannelException, InterruptedException {
if (header == null) {
Expand Down
53 changes: 17 additions & 36 deletions app/src/main/java/com/homesoft/muxer/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@
private static final String TAG = MainActivity.class.getSimpleName();
private AutoFixSurfaceView surfaceView;

private Button stream;

private TextView url;
private TextView clients;

private CameraViewModel cameraViewModel;

Expand All @@ -53,46 +52,28 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraViewModel = new ViewModelProvider.AndroidViewModelFactory(getApplication()).create(CameraViewModel.class);
stream = findViewById(R.id.stream);
stream.setOnClickListener((v)-> {
InetSocketAddress inetSocketAddress;
if (cameraViewModel.stateData.getValue() == CameraViewModel.State.STOPPED) {
cameraViewModel.startStream();
url = findViewById(R.id.url);
clients = findViewById(R.id.clients);

cameraViewModel.inetSocketAddressData.observe(this, inetSocketAddress -> {
final String text;
if (inetSocketAddress == null) {
text = getString(R.string.noWiFi);
} else {
cameraViewModel.stopStream();
inetSocketAddress = null;
text = "http://" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort();
}
url.setText(text);
});
cameraViewModel.stateData.observe(this, state -> {

cameraViewModel.connectionData.observe(this, connections -> {
final String text;
final int urlVisibility;
final boolean enabled = switch (state) {
case STREAMING -> {
text = "Stop";
urlVisibility = View.VISIBLE;
yield true;
}
case STOPPING -> {
text = "Stopping";
urlVisibility = View.INVISIBLE;
yield false;
}
case STOPPED -> {
text = "Start";
urlVisibility = View.INVISIBLE;
yield true;
}
default -> throw new IllegalArgumentException();
};
stream.setText(text);
stream.setEnabled(enabled);
InetSocketAddress inetSocketAddress = cameraViewModel.getInetSocketAddress();
if (inetSocketAddress != null) {
url.setText("http://" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort());
if (connections < 0) {
text = getString(R.string.idle);
} else {
text = connections.toString();
}
url.setVisibility(urlVisibility);
clients.setText(text);
});
url = findViewById(R.id.url);
surfaceView = findViewById(R.id.surfaceView);
surfaceView.getHolder().addCallback(this);
}
Expand Down
50 changes: 15 additions & 35 deletions app/src/main/java/com/homesoft/muxer/Remuxer.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import android.media.MediaFormat;
import android.media.MediaParser;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi;
Expand All @@ -28,6 +30,7 @@
import java.util.HashMap;
import java.util.List;

@RequiresApi(Build.VERSION_CODES.R)
public class Remuxer implements MediaParser.OutputConsumer, Runnable {
private static final int MAX_B_FRAMES = 4;
private static final String TAG = Remuxer.class.getSimpleName();
Expand Down Expand Up @@ -86,43 +89,9 @@ public void onTrackCountFound(int numberOfTracks) {

}

private static void appendByteBuffer(ByteBuffer csd, List<byte[]> list) {
if (csd != null) {
byte[] buffer = new byte[csd.limit()];
csd.rewind();
csd.get(buffer);
list.add(buffer);
}
}

@OptIn(markerClass = UnstableApi.class)
public static Format getFormat(MediaFormat mediaFormat) {
final String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
final Format.Builder builder = new Format.Builder().setSampleMimeType(mimeType);
if (mimeType.startsWith(MimeTypes.BASE_TYPE_VIDEO)) {
builder.setWidth(mediaFormat.getInteger(MediaFormat.KEY_WIDTH))
.setHeight(mediaFormat.getInteger(MediaFormat.KEY_HEIGHT));
if (mediaFormat.containsKey(MediaFormat.KEY_ROTATION)) {
builder.setRotationDegrees(mediaFormat.getInteger(MediaFormat.KEY_ROTATION));
}
} else if (mimeType.startsWith(MimeTypes.BASE_TYPE_AUDIO)) {
builder.setChannelCount(mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT))
.setSampleRate(mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
} else {
Log.d(TAG, "Unknown mimeType: " + mimeType + " " + mediaFormat);
}
final ArrayList<byte[]> csdList = new ArrayList<>(2);
ByteBuffer csd0 = mediaFormat.getByteBuffer("csd-0");
appendByteBuffer(csd0, csdList);
ByteBuffer csd1 = mediaFormat.getByteBuffer("csd-1");
appendByteBuffer(csd1, csdList);
builder.setInitializationData(csdList);
return builder.build();
}

@OptIn(markerClass = UnstableApi.class) @Override
public void onTrackDataFound(int trackIndex, @NonNull MediaParser.TrackData trackData) {
final Format format = getFormat(trackData.mediaFormat);
final Format format = CameraViewModel.getFormat(trackData.mediaFormat);
mMp4Muxer.setOrientation(format.rotationDegrees);

final Mp4Muxer.TrackToken trackToken = mMp4Muxer.addTrack(trackIndex, format);
Expand Down Expand Up @@ -196,4 +165,15 @@ public void onSampleCompleted(int trackIndex, long timeMicros, int flags, int si
}
}
}

private static Uri getUri(Context context, Uri collection, String fileName, String mimeType)
throws UnsupportedOperationException {
ContentResolver resolver = context.getContentResolver();

ContentValues newMediaValues = new ContentValues();
newMediaValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
newMediaValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);

return resolver.insert(collection, newMediaValues);
}
}
21 changes: 11 additions & 10 deletions app/src/main/res/layout-land/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,28 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<Button
android:id="@+id/stream"

<TextView
android:id="@+id/url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stream"
android:layout_marginStart="16dp"
android:text="@string/noWiFi"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/url"
app:layout_constraintStart_toEndOf="@+id/surfaceView"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintBottom_toTopOf="@id/clients"
/>

<TextView
android:id="@+id/url"
android:visibility="invisible"
android:id="@+id/clients"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text"
app:layout_constraintBottom_toBottomOf="parent"
android:text="@string/idle"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/url"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/surfaceView"
app:layout_constraintTop_toBottomOf="@+id/stream" />
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
22 changes: 12 additions & 10 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,30 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/stream"
<TextView
android:id="@+id/url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stream"
android:text="@string/noWiFi"
app:layout_constraintTop_toBottomOf="@+id/surfaceView"
app:layout_constraintBottom_toTopOf="@id/url"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintVertical_chainStyle="packed"
/>
app:layout_constraintBottom_toTopOf="@id/clients"
/>

<TextView
android:id="@+id/url"
android:id="@+id/clients"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:text="Text"
app:layout_constraintBottom_toBottomOf="parent"
android:text="@string/idle"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/url"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stream" />
app:layout_constraintBottom_toBottomOf="parent"
/>



</androidx.constraintlayout.widget.ConstraintLayout>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<resources>
<string name="app_name">Muxer</string>
<string name="noWiFi">No WiFi</string>
<string name="idle">Idle</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
* file may still have some partial data.
* </ul>
*/
@UnstableApi
public final class Mp4Muxer {
/** A token representing an added track. */
public interface TrackToken {}
Expand Down

0 comments on commit 9a0fea7

Please sign in to comment.