From e19b078ebe0fad69b3539bb40ff61ba46328a337 Mon Sep 17 00:00:00 2001 From: "brykt@google.com" Date: Thu, 13 Dec 2012 14:46:40 +0000 Subject: [PATCH] Changed so that frame_cutter takes and argument where one can specify in which interval the frames should be deleted between the first frame to cut and the last frame to cut. This can for example be used to decrease the frame rate. BUG= Review URL: https://webrtc-codereview.appspot.com/966037 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3281 4adac7df-926f-26a2-2b94-8c16560cd09d --- webrtc/tools/frame_cutter/frame_cutter.cc | 39 ++++-- webrtc/tools/frame_cutter/frame_cutter_lib.cc | 16 ++- webrtc/tools/frame_cutter/frame_cutter_lib.h | 14 +- .../frame_cutter/frame_cutter_unittest.cc | 126 ++++++++++++------ 4 files changed, 131 insertions(+), 64 deletions(-) diff --git a/webrtc/tools/frame_cutter/frame_cutter.cc b/webrtc/tools/frame_cutter/frame_cutter.cc index f89bbf3783..a1550d0275 100644 --- a/webrtc/tools/frame_cutter/frame_cutter.cc +++ b/webrtc/tools/frame_cutter/frame_cutter.cc @@ -19,22 +19,31 @@ // A command-line tool to edit a YUV-video (I420 sub-sampled). int main(int argc, char** argv) { std::string program_name = argv[0]; - std::string usage = "Deletes a series of frames in a yuv file. " - "Only I420 is supported!\n" + std::string usage = "Deletes a series of frames in a yuv file." + " Only I420 is supported!\n" "Example usage:\n" + program_name + - " --in_path=input.yuv --width=320 --height=240 --f=60 --l=120 " - "--out_path=edited_clip.yuv\n" + " --in_path=input.yuv --width=320 --height=240 --f=60 --interval=1 --l=120" + " --out_path=edited_clip.yuv\n" "Command line flags:\n" - " --in_path(string): Path and filename to the input file\n" - " -- width(int): Width in pixels of the frames in the input file." + "--in_path(string): Path and filename to the input file\n" + "--width(int): Width in pixels of the frames in the input file." " Default: -1\n" - " -- height(int): Height in pixels of the frames in the input file." + "--height(int): Height in pixels of the frames in the input file." " Default: -1\n" - " -- f(int): First frame to cut.\n" - " Default: -1\n" - " -- l(int): Last frame to cut.\n" - " Default: -1\n" - " -- out_path(string): The output file to which frames are written." + "--f(int): First frame to cut.\n" + " Default: -1\n" + "--interval(int): Set to 1 if every frame between f and l should be " + "deleted. Set it to 2 if every second frame should be deleted, " + "and so on...Frame numbering between the limits start with 1 and frames " + "read between and including the limits with number n where " + "n % interval != 0 will be kept.\n" + "Example: If the clip have frames with the numbers 1 to 10, and you set f=2" + " , l=7 and interval=2, then the output clip will contain the frames with " + " number 1, 2, 4, 6, 8, 9, 10." + " Default: 1\n" + "--l(int): Last frame to cut." + " Default: -1\n" + " --out_path(string): The output file to which frames are written." " Default: output.yuv\n"; webrtc::test::CommandLineParser parser; @@ -47,6 +56,7 @@ int main(int argc, char** argv) { parser.SetFlag("width", "-1"); parser.SetFlag("height", "-1"); parser.SetFlag("f", "-1"); + parser.SetFlag("interval", "1"); parser.SetFlag("l", "-1"); parser.SetFlag("out_path", "edited_output.yuv"); parser.SetFlag("help", "false"); @@ -61,6 +71,7 @@ int main(int argc, char** argv) { int width = strtol((parser.GetFlag("width")).c_str(), NULL, 10); int height = strtol((parser.GetFlag("height")).c_str(), NULL, 10); int first_frame_to_cut = strtol((parser.GetFlag("f")).c_str(), NULL, 10); + int interval = strtol((parser.GetFlag("interval")).c_str(), NULL, 10); int last_frame_to_cut = strtol((parser.GetFlag("l")).c_str(), NULL, 10); const char* out_path = parser.GetFlag("out_path").c_str(); @@ -80,7 +91,7 @@ int main(int argc, char** argv) { return -3; } - return webrtc::FrameCutter(in_path, width, height, first_frame_to_cut, - last_frame_to_cut, out_path); + return webrtc::CutFrames(in_path, width, height, first_frame_to_cut, + interval, last_frame_to_cut, out_path); } diff --git a/webrtc/tools/frame_cutter/frame_cutter_lib.cc b/webrtc/tools/frame_cutter/frame_cutter_lib.cc index 33597bf3cf..748970f0ec 100644 --- a/webrtc/tools/frame_cutter/frame_cutter_lib.cc +++ b/webrtc/tools/frame_cutter/frame_cutter_lib.cc @@ -21,9 +21,10 @@ using std::string; namespace webrtc { -int FrameCutter(const string& in_path, int width, int height, - int first_frame_to_cut, int last_frame_to_cut, - const string& out_path) { +int CutFrames(const string& in_path, int width, int height, + int first_frame_to_cut, int interval, int last_frame_to_cut, + const string& out_path) { + if (last_frame_to_cut < first_frame_to_cut) { fprintf(stderr, "The set of frames to cut is empty! (l < f)\n"); return -10; @@ -44,20 +45,25 @@ int FrameCutter(const string& in_path, int width, int height, if (!out_fid) { fprintf(stderr, "Could not open output file: %s.\n", out_path.c_str()); + fclose(in_fid); return -12; } int num_frames_read = 0; + int num_frames_read_between = 0; int num_bytes_read; while ((num_bytes_read = fread(temp_buffer.get(), 1, frame_length, in_fid)) == frame_length) { + num_frames_read++; if ((num_frames_read < first_frame_to_cut) || (last_frame_to_cut < num_frames_read)) { fwrite(temp_buffer.get(), 1, frame_length, out_fid); - num_frames_read++; } else { - num_frames_read++; + num_frames_read_between++; + if (num_frames_read_between % interval != 0) { + fwrite(temp_buffer.get(), 1, frame_length, out_fid); + } } } if (num_bytes_read > 0 && num_bytes_read < frame_length) { diff --git a/webrtc/tools/frame_cutter/frame_cutter_lib.h b/webrtc/tools/frame_cutter/frame_cutter_lib.h index 295a5f0f83..978e105eb0 100644 --- a/webrtc/tools/frame_cutter/frame_cutter_lib.h +++ b/webrtc/tools/frame_cutter/frame_cutter_lib.h @@ -17,11 +17,15 @@ namespace webrtc { // Frame numbering starts at 1. The set of frames to be cut includes the frame // with the number: first_frame_to_cut and last_frame_to_cut. I.e if one clip -// has 10 frames (1 to 10), and you specify first_frame_to_cut = 4 and -// last_frame_to_cut = 7, then you will get a clip that contains frame 1, 2, 3, -// 8, 9 and 10. -int FrameCutter(const std::string& in_path, int width, int height, - int first_frame_to_cut, int last_frame_to_cut, +// has 10 frames (1 to 10), and you specify first_frame_to_cut = 4, +// last_frame_to_cut = 7 and interval = 1, then you will get a clip that +// contains frame 1, 2, 3, 8, 9 and 10. +// Interval specifies with what interval frames should be cut. I.e if one clip +// has 10 frames (1 to 10), and you specify first_frame_to_cut = 1, +// last_frame_to_cut = 10 and interval = 3, then you will get a clip that +// contains frame 1, 2, 4, 5, 7, 8, 10. +int CutFrames(const std::string& in_path, int width, int height, + int first_frame_to_cut, int interval, int last_frame_to_cut, const std::string& out_path); } diff --git a/webrtc/tools/frame_cutter/frame_cutter_unittest.cc b/webrtc/tools/frame_cutter/frame_cutter_unittest.cc index ad2b588c04..b9701e1920 100644 --- a/webrtc/tools/frame_cutter/frame_cutter_unittest.cc +++ b/webrtc/tools/frame_cutter/frame_cutter_unittest.cc @@ -19,7 +19,7 @@ #include "webrtc/tools/frame_cutter/frame_cutter_lib.h" using webrtc::CalcBufferSize; -using webrtc::FrameCutter; +using webrtc::CutFrames; using webrtc::kI420; using webrtc::scoped_array; using webrtc::test::OutputPath; @@ -28,80 +28,126 @@ using webrtc::test::ResourcePath; namespace webrtc { namespace test { -const int width = 352; -const int height = 288; +const int kWidth = 352; +const int kHeight = 288; -const std::string ref_video = ResourcePath("foreman_cif", "yuv"); -const std::string test_video = OutputPath() + "testvideo.yuv"; +const std::string kRefVideo = ResourcePath("foreman_cif", "yuv"); +const std::string kTestVideo = OutputPath() + "testvideo.yuv"; int num_bytes_read; -TEST(FrameCutterUnittest, ValidInPath) { - const int first_frame_to_cut = 160; - const int last_frame_to_cut = 240; +TEST(CutFramesUnittest, ValidInPath) { + const int kFirstFrameToCut = 160; + const int kInterval = 1; + const int kLastFrameToCut = 240; - int result = FrameCutter(ref_video, width, height, first_frame_to_cut, - last_frame_to_cut, test_video); + int result = CutFrames(kRefVideo, kWidth, kHeight, kFirstFrameToCut, + kInterval, kLastFrameToCut, kTestVideo); EXPECT_EQ(0, result); - FILE* ref_video_fid = fopen(ref_video.c_str(), "rb"); + FILE* ref_video_fid = fopen(kRefVideo.c_str(), "rb"); ASSERT_TRUE(ref_video_fid != NULL); - FILE* test_video_fid = fopen(test_video.c_str(), "rb"); + FILE* test_video_fid = fopen(kTestVideo.c_str(), "rb"); ASSERT_TRUE(test_video_fid != NULL); - const int frame_size =CalcBufferSize(kI420, width, height); + const int kFrameSize = CalcBufferSize(kI420, kWidth, kHeight); - scoped_array ref_buffer(new int[frame_size]); - scoped_array test_buffer(new int[frame_size]); + scoped_array ref_buffer(new int[kFrameSize]); + scoped_array test_buffer(new int[kFrameSize]); - for (int i = 0; i < first_frame_to_cut; ++i) { - num_bytes_read = fread(ref_buffer.get(), 1, frame_size, ref_video_fid); - EXPECT_EQ(frame_size, num_bytes_read); + for (int i = 1; i < kFirstFrameToCut; ++i) { + num_bytes_read = fread(ref_buffer.get(), 1, kFrameSize, ref_video_fid); + EXPECT_EQ(kFrameSize, num_bytes_read); - num_bytes_read = fread(test_buffer.get(), 1, frame_size, test_video_fid); - EXPECT_EQ(frame_size, num_bytes_read); + num_bytes_read = fread(test_buffer.get(), 1, kFrameSize, test_video_fid); + EXPECT_EQ(kFrameSize, num_bytes_read); - EXPECT_EQ(0, memcmp(ref_buffer.get(), test_buffer.get(), frame_size)); + EXPECT_EQ(0, memcmp(ref_buffer.get(), test_buffer.get(), kFrameSize)); } // Do not compare the frames that have been cut. - for (int i = first_frame_to_cut; i <= last_frame_to_cut; ++i) { - num_bytes_read = fread(ref_buffer.get(), 1, frame_size, ref_video_fid); - EXPECT_EQ(frame_size, num_bytes_read); + for (int i = kFirstFrameToCut; i <= kLastFrameToCut; ++i) { + num_bytes_read = fread(ref_buffer.get(), 1, kFrameSize, ref_video_fid); + EXPECT_EQ(kFrameSize, num_bytes_read); } while (!feof(test_video_fid) && !feof(ref_video_fid)) { - num_bytes_read = fread(ref_buffer.get(), 1, frame_size, ref_video_fid); + num_bytes_read = fread(ref_buffer.get(), 1, kFrameSize, ref_video_fid); if (!feof(ref_video_fid)) { - EXPECT_EQ(frame_size, num_bytes_read); + EXPECT_EQ(kFrameSize, num_bytes_read); } - num_bytes_read = fread(test_buffer.get(), 1, frame_size, test_video_fid); + num_bytes_read = fread(test_buffer.get(), 1, kFrameSize, test_video_fid); if (!feof(test_video_fid)) { - EXPECT_EQ(frame_size, num_bytes_read); + EXPECT_EQ(kFrameSize, num_bytes_read); } if (!feof(test_video_fid) && !feof(test_video_fid)) { - EXPECT_EQ(0, memcmp(ref_buffer.get(), test_buffer.get(), frame_size)); + EXPECT_EQ(0, memcmp(ref_buffer.get(), test_buffer.get(), kFrameSize)); } } + fclose(ref_video_fid); + fclose(test_video_fid); } -TEST(FrameCutterUnittest, EmptySetToCut) { - int first_frame_to_cut = 2; - int last_frame_to_cut = 1; +TEST(CutFramesUnittest, EmptySetToCut) { + const int kFirstFrameToCut = 2; + const int kInterval = 1; + const int kLastFrameToCut = 1; - int result = FrameCutter(ref_video, width, height, first_frame_to_cut, - last_frame_to_cut, test_video); + int result = CutFrames(kRefVideo, kWidth, kHeight, kFirstFrameToCut, + kInterval, kLastFrameToCut, kTestVideo); EXPECT_EQ(-10, result); } -TEST(FrameCutterUnittest, InValidInPath) { - const std::string ref_video = "PATH/THAT/DOES/NOT/EXIST"; +TEST(CutFramesUnittest, InValidInPath) { + const std::string kRefVideo = "PATH/THAT/DOES/NOT/EXIST"; - int first_frame_to_cut = 30; - int last_frame_to_cut = 120; + const int kFirstFrameToCut = 30; + const int kInterval = 1; + const int kLastFrameToCut = 120; - int result = FrameCutter(ref_video, width, height, first_frame_to_cut, - last_frame_to_cut, test_video); + int result = CutFrames(kRefVideo, kWidth, kHeight, kFirstFrameToCut, + kInterval, kLastFrameToCut, kTestVideo); EXPECT_EQ(-11, result); } + +TEST(CutFramesUnitTest, DeletingEverySecondFrame) { + const int kFirstFrameToCut = 1; + const int kInterval = 2; + const int kLastFrameToCut = 10000; + // Set kLastFrameToCut to a large value so that all frame are processed. + int result = CutFrames(kRefVideo, kWidth, kHeight, kFirstFrameToCut, + kInterval, kLastFrameToCut, kTestVideo); + EXPECT_EQ(0, result); + + FILE* original_fid = fopen(kRefVideo.c_str(), "rb"); + ASSERT_TRUE(original_fid != NULL); + FILE* edited_fid = fopen(kTestVideo.c_str(), "rb"); + ASSERT_TRUE(edited_fid != NULL); + + const int kFrameSize = CalcBufferSize(kI420, kWidth, kHeight); + + scoped_array original_buffer(new int[kFrameSize]); + scoped_array edited_buffer(new int[kFrameSize]); + + int num_frames_read = 0; + + while (!feof(original_fid) && !feof(edited_fid)) { + num_bytes_read = + fread(original_buffer.get(), 1, kFrameSize, original_fid); + if (!feof(original_fid)) { + EXPECT_EQ(kFrameSize, num_bytes_read); + num_frames_read++; + } + if (num_frames_read % kInterval != 0) { + num_bytes_read = fread(edited_buffer.get(), 1, kFrameSize, edited_fid); + if (!feof(edited_fid)) { + EXPECT_EQ(kFrameSize, num_bytes_read); + } + if (!feof(original_fid) && !feof(edited_fid)) { + EXPECT_EQ(0, memcmp(original_buffer.get(), + edited_buffer.get(), kFrameSize)); + } + } + } +} } }