OpenShot Library | libopenshot  0.1.9
ChunkWriter.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @brief Source file for ChunkWriter class
4  * @author Jonathan Thomas <jonathan@openshot.org>
5  *
6  * @section LICENSE
7  *
8  * Copyright (c) 2008-2014 OpenShot Studios, LLC
9  * <http://www.openshotstudios.com/>. This file is part of
10  * OpenShot Library (libopenshot), an open-source project dedicated to
11  * delivering high quality video editing and animation solutions to the
12  * world. For more information visit <http://www.openshot.org/>.
13  *
14  * OpenShot Library (libopenshot) is free software: you can redistribute it
15  * and/or modify it under the terms of the GNU Lesser General Public License
16  * as published by the Free Software Foundation, either version 3 of the
17  * License, or (at your option) any later version.
18  *
19  * OpenShot Library (libopenshot) is distributed in the hope that it will be
20  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
26  */
27 
28 #include "../include/ChunkWriter.h"
29 
30 using namespace openshot;
31 
32 ChunkWriter::ChunkWriter(string path, ReaderBase *reader) :
33  local_reader(reader), path(path), chunk_size(24*3), chunk_count(1), frame_count(1), is_writing(false),
34  default_extension(".webm"), default_vcodec("libvpx"), default_acodec("libvorbis"), last_frame_needed(false), is_open(false)
35 {
36  // Change codecs to default
37  info.vcodec = default_vcodec;
38  info.acodec = default_acodec;
39 
40  // Copy info struct from the source reader
41  CopyReaderInfo(local_reader);
42 
43  // Create folder (if it does not exist)
44  create_folder(path);
45 
46  // Write JSON meta data file
47  write_json_meta_data();
48 
49  // Open reader
50  local_reader->Open();
51 }
52 
53 // get a formatted path of a specific chunk
54 string ChunkWriter::get_chunk_path(int64_t chunk_number, string folder, string extension)
55 {
56  // Create path of new chunk video
57  stringstream chunk_count_string;
58  chunk_count_string << chunk_number;
59  QString padded_count = "%1"; //chunk_count_string.str().c_str();
60  padded_count = padded_count.arg(chunk_count_string.str().c_str(), 6, '0');
61  if (folder.length() != 0 && extension.length() != 0)
62  // Return path with FOLDER and EXTENSION name
63  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + folder.c_str() + QDir::separator() + padded_count + extension.c_str()).toStdString();
64 
65  else if (folder.length() == 0 && extension.length() != 0)
66  // Return path with NO FOLDER and EXTENSION name
67  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + padded_count + extension.c_str()).toStdString();
68 
69  else if (folder.length() != 0 && extension.length() == 0)
70  // Return path with FOLDER and NO EXTENSION
71  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + folder.c_str()).toStdString();
72  else
73  return "";
74 }
75 
76 // Add a frame to the queue waiting to be encoded.
77 void ChunkWriter::WriteFrame(std::shared_ptr<Frame> frame)
78 {
79  // Check for open reader (or throw exception)
80  if (!is_open)
81  throw WriterClosed("The ChunkWriter is closed. Call Open() before calling this method.", path);
82 
83  // Check if currently writing chunks?
84  if (!is_writing)
85  {
86  // Save thumbnail of chunk start frame
87  frame->Save(get_chunk_path(chunk_count, "", ".jpeg"), 1.0);
88 
89  // Create FFmpegWriter (FINAL quality)
90  create_folder(get_chunk_path(chunk_count, "final", ""));
91  writer_final = new FFmpegWriter(get_chunk_path(chunk_count, "final", default_extension));
92  writer_final->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
93  writer_final->SetVideoOptions(true, default_vcodec, info.fps, info.width, info.height, info.pixel_ratio, false, false, info.video_bit_rate);
94 
95  // Create FFmpegWriter (PREVIEW quality)
96  create_folder(get_chunk_path(chunk_count, "preview", ""));
97  writer_preview = new FFmpegWriter(get_chunk_path(chunk_count, "preview", default_extension));
98  writer_preview->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
99  writer_preview->SetVideoOptions(true, default_vcodec, info.fps, info.width * 0.5, info.height * 0.5, info.pixel_ratio, false, false, info.video_bit_rate * 0.5);
100 
101  // Create FFmpegWriter (LOW quality)
102  create_folder(get_chunk_path(chunk_count, "thumb", ""));
103  writer_thumb = new FFmpegWriter(get_chunk_path(chunk_count, "thumb", default_extension));
104  writer_thumb->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
105  writer_thumb->SetVideoOptions(true, default_vcodec, info.fps, info.width * 0.25, info.height * 0.25, info.pixel_ratio, false, false, info.video_bit_rate * 0.25);
106 
107  // Prepare Streams
108  writer_final->PrepareStreams();
109  writer_preview->PrepareStreams();
110  writer_thumb->PrepareStreams();
111 
112  // Write header
113  writer_final->WriteHeader();
114  writer_preview->WriteHeader();
115  writer_thumb->WriteHeader();
116 
117  // Keep track that a chunk is being written
118  is_writing = true;
119  last_frame_needed = true;
120  }
121 
122  // If this is not the 1st chunk, always start frame 1 with the last frame from the previous
123  // chunk. This helps to prevent audio resampling issues (because it "stokes" the sample array)
124  if (last_frame_needed)
125  {
126  if (last_frame)
127  {
128  // Write the previous chunks LAST FRAME to the current chunk
129  writer_final->WriteFrame(last_frame);
130  writer_preview->WriteFrame(last_frame);
131  writer_thumb->WriteFrame(last_frame);
132  } else {
133  // Write the 1st frame (of the 1st chunk)... since no previous chunk is available
134  std::shared_ptr<Frame> blank_frame(new Frame(1, info.width, info.height, "#000000", info.sample_rate, info.channels));
135  blank_frame->AddColor(info.width, info.height, "#000000");
136  writer_final->WriteFrame(blank_frame);
137  writer_preview->WriteFrame(blank_frame);
138  writer_thumb->WriteFrame(blank_frame);
139  }
140 
141  // disable last frame
142  last_frame_needed = false;
143  }
144 
145 
146  //////////////////////////////////////////////////
147  // WRITE THE CURRENT FRAME TO THE CURRENT CHUNK
148  writer_final->WriteFrame(frame);
149  writer_preview->WriteFrame(frame);
150  writer_thumb->WriteFrame(frame);
151  //////////////////////////////////////////////////
152 
153 
154  // Write the frames once it reaches the correct chunk size
155  if (frame_count % chunk_size == 0 && frame_count >= chunk_size)
156  {
157  cout << "Done with chunk" << endl;
158  cout << "frame_count: " << frame_count << endl;
159  cout << "chunk_size: " << chunk_size << endl;
160 
161  // Pad an additional 12 frames
162  for (int z = 0; z<12; z++)
163  {
164  // Repeat frame
165  writer_final->WriteFrame(frame);
166  writer_preview->WriteFrame(frame);
167  writer_thumb->WriteFrame(frame);
168  }
169 
170  // Write Footer
171  writer_final->WriteTrailer();
172  writer_preview->WriteTrailer();
173  writer_thumb->WriteTrailer();
174 
175  // Close writer & reader
176  writer_final->Close();
177  writer_preview->Close();
178  writer_thumb->Close();
179 
180  // Increment chunk count
181  chunk_count++;
182 
183  // Stop writing chunk
184  is_writing = false;
185  }
186 
187  // Increment frame counter
188  frame_count++;
189 
190  // Keep track of the last frame added
191  last_frame = frame;
192 }
193 
194 
195 // Write a block of frames from a reader
196 void ChunkWriter::WriteFrame(ReaderBase* reader, int64_t start, int64_t length)
197 {
198  // Loop through each frame (and encoded it)
199  for (int64_t number = start; number <= length; number++)
200  {
201  // Get the frame
202  std::shared_ptr<Frame> f = reader->GetFrame(number);
203 
204  // Encode frame
205  WriteFrame(f);
206  }
207 }
208 
209 // Write a block of frames from the local cached reader
210 void ChunkWriter::WriteFrame(int64_t start, int64_t length)
211 {
212  // Loop through each frame (and encoded it)
213  for (int64_t number = start; number <= length; number++)
214  {
215  // Get the frame
216  std::shared_ptr<Frame> f = local_reader->GetFrame(number);
217 
218  // Encode frame
219  WriteFrame(f);
220  }
221 }
222 
223 // Close the writer
225 {
226  // Write the frames once it reaches the correct chunk size
227  if (is_writing)
228  {
229  cout << "Final chunk" << endl;
230  cout << "frame_count: " << frame_count << endl;
231  cout << "chunk_size: " << chunk_size << endl;
232 
233  // Pad an additional 12 frames
234  for (int z = 0; z<12; z++)
235  {
236  // Repeat frame
237  writer_final->WriteFrame(last_frame);
238  writer_preview->WriteFrame(last_frame);
239  writer_thumb->WriteFrame(last_frame);
240  }
241 
242  // Write Footer
243  writer_final->WriteTrailer();
244  writer_preview->WriteTrailer();
245  writer_thumb->WriteTrailer();
246 
247  // Close writer & reader
248  writer_final->Close();
249  writer_preview->Close();
250  writer_thumb->Close();
251 
252  // Increment chunk count
253  chunk_count++;
254 
255  // Stop writing chunk
256  is_writing = false;
257  }
258 
259  // close writer
260  is_open = false;
261 
262  // Reset frame counters
263  chunk_count = 0;
264  frame_count = 0;
265 
266  // Open reader
267  local_reader->Close();
268 }
269 
270 // write JSON meta data
271 void ChunkWriter::write_json_meta_data()
272 {
273  // Load path of chunk folder
274  string json_path = QDir::cleanPath(QString(path.c_str()) + QDir::separator() + "info.json").toStdString();
275 
276  // Write JSON file
277  ofstream myfile;
278  myfile.open (json_path.c_str());
279  myfile << local_reader->Json() << endl;
280  myfile.close();
281 }
282 
283 // check for chunk folder
284 void ChunkWriter::create_folder(string path)
285 {
286  QDir dir(path.c_str());
287  if (!dir.exists()) {
288  dir.mkpath(".");
289  }
290 }
291 
292 // check for valid chunk json
293 bool ChunkWriter::is_chunk_valid()
294 {
295  return true;
296 }
297 
298 // Open the writer
300 {
301  is_open = true;
302 }
303 
304 
ChunkWriter(string path, ReaderBase *reader)
Constructor for ChunkWriter. Throws one of the following exceptions.
Definition: ChunkWriter.cpp:32
int channels
The number of audio channels used in the audio stream.
Definition: WriterBase.h:72
WriterInfo info
Information about the current media file.
Definition: WriterBase.h:92
int video_bit_rate
The bit rate of the video stream (in bytes)
Definition: WriterBase.h:60
Fraction pixel_ratio
The pixel ratio of the video stream as a fraction (i.e. some pixels are not square) ...
Definition: WriterBase.h:61
void CopyReaderInfo(ReaderBase *reader)
This method copy&#39;s the info struct of a reader, and sets the writer with the same info...
Definition: WriterBase.cpp:64
This class represents a single frame of video (i.e. image & audio data)
Definition: Frame.h:115
string acodec
The name of the audio codec used to encode / decode the video stream.
Definition: WriterBase.h:69
This class uses the FFmpeg libraries, to write and encode video files and audio files.
Definition: FFmpegWriter.h:143
string vcodec
The name of the video codec used to encode / decode the video stream.
Definition: WriterBase.h:63
virtual void Close()=0
Close the reader (and any resources it was consuming)
This abstract class is the base class, used by all readers in libopenshot.
Definition: ReaderBase.h:95
int width
The width of the video (in pixels)
Definition: WriterBase.h:57
void WriteFrame(std::shared_ptr< Frame > frame)
Add a frame to the stack waiting to be encoded.
void SetVideoOptions(bool has_video, string codec, Fraction fps, int width, int height, Fraction pixel_ratio, bool interlaced, bool top_field_first, int bit_rate)
Set video export options.
virtual std::shared_ptr< Frame > GetFrame(int64_t number)=0
void WriteTrailer()
Write the file trailer (after all frames are written). This is called automatically by the Close() me...
void WriteHeader()
Write the file header (after the options are set). This method is called automatically by the Open() ...
void Close()
Close the writer.
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: WriterBase.h:71
Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: WriterBase.h:59
This namespace is the default namespace for all code in the openshot library.
virtual string Json()=0
Get and Set JSON methods.
void SetAudioOptions(bool has_audio, string codec, int sample_rate, int channels, ChannelLayout channel_layout, int bit_rate)
Set audio export options.
void WriteFrame(std::shared_ptr< Frame > frame)
Add a frame to the stack waiting to be encoded.
Definition: ChunkWriter.cpp:77
Exception when a writer is closed, and a frame is requested.
Definition: Exceptions.h:264
ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
Definition: WriterBase.h:73
void PrepareStreams()
Prepare & initialize streams and open codecs. This method is called automatically by the Open() metho...
int height
The height of the video (in pixels)
Definition: WriterBase.h:56
void Open()
Open writer.
void Close()
Close the writer.
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)