Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ private void continueSelectedDownload(@NonNull final StoredFileHelper storage) {
final Stream selectedStream;
Stream secondaryStream = null;
final char kind;
final boolean embedMetadata = dialogBinding.metadataSwitch.isChecked();
int threads = dialogBinding.threads.getProgress() + 1;
final String[] urls;
final List<MissionRecoveryInfo> recoveryInfo;
Expand All @@ -1049,11 +1050,12 @@ private void continueSelectedDownload(@NonNull final StoredFileHelper storage) {
kind = 'a';
selectedStream = audioStreamsAdapter.getItem(selectedAudioIndex);

if (selectedStream.getFormat() == MediaFormat.M4A) {
psName = Postprocessing.ALGORITHM_M4A_NO_DASH;
} else if (selectedStream.getFormat() == MediaFormat.WEBMA_OPUS) {
psName = Postprocessing.ALGORITHM_OGG_FROM_WEBM_DEMUXER;
}
psName = switch (selectedStream.getFormat()) {
case M4A -> Postprocessing.ALGORITHM_M4A_NO_DASH;
case WEBMA_OPUS -> Postprocessing.ALGORITHM_OGG_FROM_WEBM_DEMUXER;
case MP3 -> Postprocessing.ALGORITHM_MP3_METADATA;
default -> null;
};
} else if (checkedRadioButtonId == R.id.video_button) {
kind = 'v';
selectedStream = videoStreamsAdapter.getItem(selectedVideoIndex);
Expand All @@ -1079,6 +1081,8 @@ private void continueSelectedDownload(@NonNull final StoredFileHelper storage) {
if (secondary.getSizeInBytes() > 0 && videoSize > 0) {
nearLength = secondary.getSizeInBytes() + videoSize;
}
} else if (selectedStream.getFormat() == MediaFormat.MPEG_4) {
psName = Postprocessing.ALGORITHM_MP4_METADATA;
}
} else if (checkedRadioButtonId == R.id.subtitle_button) {
threads = 1; // use unique thread for subtitles due small file size
Expand Down Expand Up @@ -1116,8 +1120,8 @@ private void continueSelectedDownload(@NonNull final StoredFileHelper storage) {
);
}

DownloadManagerService.startMission(context, urls, storage, kind, threads,
currentInfo, psName, psArgs, nearLength, new ArrayList<>(recoveryInfo));
DownloadManagerService.startMission(context, urls, storage, kind, threads, currentInfo,
psName, embedMetadata, psArgs, nearLength, new ArrayList<>(recoveryInfo));

Toast.makeText(context, getString(R.string.download_has_started),
Toast.LENGTH_SHORT).show();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ protected void addImagesMetadataItem(final LayoutInflater inflater,
|| image.getWidth() != Image.WIDTH_UNKNOWN
// if even the resolution level is unknown, ?x? will be shown
|| image.getEstimatedResolutionLevel() == Image.ResolutionLevel.UNKNOWN) {
urls.append(imageSizeToText(image.getHeight()));
urls.append('x');
urls.append(imageSizeToText(image.getWidth()));
urls.append('x');
urls.append(imageSizeToText(image.getHeight()));
} else {
switch (image.getEstimatedResolutionLevel()) {
case LOW -> urls.append(getString(R.string.image_quality_low));
Expand Down
95 changes: 74 additions & 21 deletions app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.schabi.newpipe.streams;

import android.graphics.Bitmap;

import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.streams.Mp4DashReader.Hdlr;
import org.schabi.newpipe.streams.Mp4DashReader.Mdia;
import org.schabi.newpipe.streams.Mp4DashReader.Mp4DashChunk;
Expand All @@ -13,7 +16,17 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;

import us.shandian.giga.postprocessing.Mp4MetadataHelper;

/**
* MP4 muxer that builds a standard MP4 file from DASH fragmented MP4 sources.
*
* @see <a href="https://atomicparsley.sourceforge.net/mpeg-4files.html">
* https://atomicparsley.sourceforge.net/mpeg-4files.html</a> for a quick summary on
* the MP4 file format and its specification.
* @see <a href="https://developer.apple.com/documentation/quicktime-file-format/">
* Apple Quick Time Format Specification</a> which is the basis for MP4 file format
* and contains detailed information about the structure of MP4 files.
* @author kapodamy
*/
public class Mp4FromDashWriter {
Expand Down Expand Up @@ -50,13 +63,41 @@ public class Mp4FromDashWriter {

private final ArrayList<Integer> compatibleBrands = new ArrayList<>(5);

public Mp4FromDashWriter(final SharpStream... sources) throws IOException {

private final boolean embedMetadata;
private final Mp4MetadataHelper metadataHelper;

public Mp4FromDashWriter(final boolean embedMetadata,
final StreamInfo streamInfo,
final Bitmap thumbnail,
final SharpStream... sources) throws IOException {
for (final SharpStream src : sources) {
if (!src.canRewind() && !src.canRead()) {
throw new IOException("All sources must be readable and allow rewind");
}
}

this.embedMetadata = embedMetadata;
this.metadataHelper = new Mp4MetadataHelper(
this::auxOffset,
buffer -> {
try {
auxWrite(buffer);
} catch (final IOException e) {
throw new RuntimeException(e);
}
},
offset -> {
try {
return lengthFor(offset);
} catch (final IOException e) {
throw new RuntimeException(e);
}
},
streamInfo,
thumbnail
);

sourceTracks = sources;
readers = new Mp4DashReader[sourceTracks.length];
readersChunks = new Mp4DashChunk[readers.length];
Expand Down Expand Up @@ -712,10 +753,14 @@ private int makeMoov(final int[] defaultMediaTime, final TablesInfo[] tablesInfo

makeMvhd(longestTrack);

if (embedMetadata) {
metadataHelper.makeUdta();
}

for (int i = 0; i < tracks.length; i++) {
if (tracks[i].trak.tkhd.matrix.length != 36) {
throw
new RuntimeException("bad track matrix length (expected 36) in track n°" + i);
throw new RuntimeException(
"bad track matrix length (expected 36) in track n°" + i);
}
makeTrak(i, durations[i], defaultMediaTime[i], tablesInfo[i], is64);
}
Expand Down Expand Up @@ -763,7 +808,7 @@ private void makeTrak(final int index, final long duration, final int defaultMed
final int mediaTime;

if (tracks[index].trak.edstElst == null) {
// is a audio track ¿is edst/elst optional for audio tracks?
// is an audio track; is edst/elst optional for audio tracks?
mediaTime = 0x00; // ffmpeg set this value as zero, instead of defaultMediaTime
bMediaRate = 0x00010000;
} else {
Expand Down Expand Up @@ -871,33 +916,41 @@ private int makeSbgp() throws IOException {
return offset + 0x14;
}

/**
* Creates a Sample Group Description Box.
*
* <p>
* What does it do?
* <br>
* The table inside of this box gives information about the
* characteristics of sample groups. The descriptive information is any other
* information needed to define or characterize the sample group.
* </p>
*
* <p>
* ¿is replicable this box?
* <br>
* NO due lacks of documentation about this box but...
* most of m4a encoders and ffmpeg uses this box with dummy values (same values)
* </p>
*
* @return byte array with the 'sgpd' box
*/
private byte[] makeSgpd() {
/*
* Sample Group Description Box
*
* ¿whats does?
* the table inside of this box gives information about the
* characteristics of sample groups. The descriptive information is any other
* information needed to define or characterize the sample group.
*
* ¿is replicable this box?
* NO due lacks of documentation about this box but...
* most of m4a encoders and ffmpeg uses this box with dummy values (same values)
*/

final ByteBuffer buffer = ByteBuffer.wrap(new byte[] {
0x00, 0x00, 0x00, 0x1A, // box size
0x73, 0x67, 0x70, 0x64, // "sgpd"
0x01, 0x00, 0x00, 0x00, // box flags (unknown flag sets)
0x72, 0x6F, 0x6C, 0x6C, // ¿¿group type??
0x00, 0x00, 0x00, 0x02, // ¿¿??
0x00, 0x00, 0x00, 0x01, // ¿¿??
(byte) 0xFF, (byte) 0xFF // ¿¿??
0x72, 0x6F, 0x6C, 0x6C, // group type??
0x00, 0x00, 0x00, 0x02, // ??
0x00, 0x00, 0x00, 0x01, // ??
(byte) 0xFF, (byte) 0xFF // ??
});

return buffer.array();
}


static class TablesInfo {
int stts;
int stsc;
Expand Down
Loading