Internal change
PiperOrigin-RevId: 182603092
Change-Id: I852b2533e4103e0638c1031afc883bbab89bb160
diff --git a/example/muxing/camm_muxing.cc b/example/muxing/camm_muxing.cc
index ba75725..147a438 100644
--- a/example/muxing/camm_muxing.cc
+++ b/example/muxing/camm_muxing.cc
@@ -1,16 +1,25 @@
/*
- * This code sample muxes an artificially generated video stream with a
- * data stream for camera motion metadata (CAMM) into a single mp4 file.
+ * This code sample muxes a list of jpegs with an artificially generated data
+ * stream for camera motion metadata (CAMM) into a single mp4 file.
* The sample can be built with the "make" command.
* After building the sample, it can be run as follows:
*
- * > ./camm_muxing output_file.mp4
+ * > ./camm_muxing /path/to/frames/ num_of_frames output_file.mp4
*
- * Note that this must be run in the sample directory as the "sample.jpg" file.
+ * /path/to/frames/ is the directory of the input jpeg files. We assume the
+ * indexing is 1-based, and the file names are
+ * 00001.jpg
+ * 00002.jpg
+ * 00003.jpg
+ * ...
*
- * The video stream is a single JPEG file for the entire video duration. It
- * is for illustration purposes only. The calling code is expected to
- * already have the encoded bytes of video frames to mux into the mp4 file.
+ * num_of_frames is an integer that represents the number of jpegs in the
+ * directory. For example, num_of_frames = 50 means 00001.jpg to 00050.jpg in
+ * the input directory are imported in the muxing.
+ *
+ * output_file.mp4 is the output mp4 file which will consist of two streams:
+ * - A mjpeg video stream.
+ * - A stream of an artifially generated CAMM track.
*/
#include <endian.h>
@@ -22,13 +31,14 @@
#include <cstring>
#include <fstream>
#include <iostream>
+#include <string>
#include <vector>
#define FRAME_WIDTH 2880
#define FRAME_HEIGHT 1800
-#define VIDEO_TIME_SECONDS 10
+#define VIDEO_FRAME_RATE 5
#define VIDEO_TIME_SCALE 90000
-#define NUM_CAMM_SAMPLES 200
+#define CAMM_SAMPLE_RATE 200
#define BYTE_BUFFER_SIZE 1000
namespace {
@@ -206,7 +216,7 @@
if (!MP4WriteSample(handle, video_track_id,
reinterpret_cast<const uint8_t *>(jpg_bytes.data()),
/* num_bytes */ jpg_bytes.size(),
- /* duration */ VIDEO_TIME_SCALE,
+ /* duration */ VIDEO_TIME_SCALE / VIDEO_FRAME_RATE,
/* rendering_offset */ pts,
/* is_sync_sample */ true)) {
printf("Failed to write mp4 sample to video track\n");
@@ -214,33 +224,64 @@
}
}
+static std::string GetJpegFilename(const char *dirname, int frame_index) {
+ char buffer[500];
+ snprintf(buffer, sizeof(buffer), "%s/%05d.jpg", dirname, frame_index);
+ std::string filename(buffer);
+ return filename;
+}
+
+// Reads the contents of the jpeg file into memory.
+static std::vector<char> ReadJpegFileBytes(const char *filename) {
+ struct stat buffer;
+ if (stat(filename, &buffer) != 0) {
+ printf(
+ "Sample file '%s' does not exist.\n"
+ "Make sure it exists and then run the program again.\n",
+ filename);
+ exit(1);
+ }
+ std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
+ std::ifstream::pos_type pos = ifs.tellg();
+ std::vector<char> result(pos);
+ ifs.seekg(0, std::ios::beg);
+ ifs.read(&result[0], pos);
+ return result;
+}
+
static void WriteStreams(MP4FileHandle handle, int video_track_id,
- const std::vector<char> &jpg_bytes,
+ const char *dirname, int num_frames,
int camm_track_id) {
char camm_buffer[BYTE_BUFFER_SIZE];
+
int video_pts = 0;
int camm_pts = 0;
- int camm_packet_num = 0;
- int max_pts = VIDEO_TIME_SECONDS * VIDEO_TIME_SCALE;
- while (video_pts < max_pts && camm_pts < max_pts) {
+ int frame_index = 1;
+ int camm_packet_index = 0;
+
+ while (frame_index <= num_frames) {
if (video_pts <= camm_pts) {
- WriteVideoFrame(handle, video_track_id, jpg_bytes, video_pts,
+ std::string filename = GetJpegFilename(dirname, frame_index);
+ std::vector<char> bytes = ReadJpegFileBytes(filename.c_str());
+ WriteVideoFrame(handle, video_track_id, bytes, video_pts,
VIDEO_TIME_SCALE);
- video_pts += VIDEO_TIME_SCALE;
+ video_pts += VIDEO_TIME_SCALE / VIDEO_FRAME_RATE;
+ frame_index++;
} else {
- WriteCammPacket(handle, camm_track_id, camm_pts, camm_packet_num,
+ WriteCammPacket(handle, camm_track_id, camm_pts, camm_packet_index,
camm_buffer);
- camm_pts += max_pts / NUM_CAMM_SAMPLES;
- camm_packet_num++;
+ camm_pts += VIDEO_TIME_SCALE / CAMM_SAMPLE_RATE;
+ camm_packet_index++;
}
}
}
-static int AddVideoTrack(MP4FileHandle handle) {
+static int AddVideoTrack(MP4FileHandle handle, int num_frames) {
printf("Adding video track\n");
- int video_track_id = MP4AddVideoTrack(
- handle, VIDEO_TIME_SCALE, VIDEO_TIME_SCALE * VIDEO_TIME_SECONDS,
- FRAME_WIDTH, FRAME_HEIGHT, MP4_JPEG_VIDEO_TYPE);
+ int video_track_id =
+ MP4AddVideoTrack(handle, VIDEO_TIME_SCALE,
+ VIDEO_TIME_SCALE * num_frames / VIDEO_FRAME_RATE,
+ FRAME_WIDTH, FRAME_HEIGHT, MP4_JPEG_VIDEO_TYPE);
if (video_track_id == MP4_INVALID_TRACK_ID) {
printf("Can't add video track\n");
exit(1);
@@ -262,29 +303,13 @@
return data_track_id;
}
-// Reads the contents of the sample jpeg file into memory.
-static std::vector<char> ReadSampleFileBytes() {
- struct stat buffer;
- const char *filename = "sample.jpg";
- if (stat(filename, &buffer) != 0) {
- printf(
- "Sample file 'sample.jpg' doesn't exist\n"
- "Make sure it is in the directory and then run the program\n"
- "again.\n");
- exit(1);
- }
- std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
- std::ifstream::pos_type pos = ifs.tellg();
- std::vector<char> result(pos);
- ifs.seekg(0, std::ios::beg);
- ifs.read(&result[0], pos);
- return result;
-}
-
static void VerifyProgramArgs(int argc, char **argv) {
- if (argc != 2) {
+ if (argc != 4) {
printf(
- "Usage: %s file.mp4\n"
+ "Usage: %s frames/ 50 file.mp4\n"
+ "frames/ is the input directory of frames\n"
+ "50 is the number of frames in the input directory to put in the "
+ "video, i.e. 00001.jpg to 00050.jpg\n"
"file.mp4 is the output location of the generated video\n",
argv[0]);
exit(1);
@@ -302,12 +327,12 @@
#endif
VerifyProgramArgs(argc, argv);
- std::vector<char> jpg_bytes = ReadSampleFileBytes();
- MP4FileHandle handle = MP4Create(argv[1], /* flags */ 0);
- int video_track_id = AddVideoTrack(handle);
+ MP4FileHandle handle = MP4Create(argv[3], /* flags */ 0);
+ int num_frames = std::stoi(argv[2]);
+ int video_track_id = AddVideoTrack(handle, num_frames);
int camm_track_id = AddCammTrack(handle);
- WriteStreams(handle, video_track_id, jpg_bytes, camm_track_id);
+ WriteStreams(handle, video_track_id, argv[1], num_frames, camm_track_id);
printf("Dumping MP4 file information:\n");
MP4Dump(handle);
diff --git a/example/muxing/sample.jpg b/example/muxing/sample.jpg
deleted file mode 100644
index c76d552..0000000
--- a/example/muxing/sample.jpg
+++ /dev/null
Binary files differ