8 #include "interlacemodes.h"
10 #include "mainerror.h"
20 FileYUV::FileYUV(Asset *asset, File *file)
21 : FileBase(asset, file)
23 if (asset->format == FILE_UNKNOWN) asset->format = FILE_YUV;
24 asset->byte_order = 0; // FUTURE: is this always correct?
27 stream = new YUVStream();
33 // NOTE: close_file() is already called
37 int FileYUV::open_file(int should_read, int should_write)
43 result = stream->open_read(asset->path);
44 if (result) return result;
46 // NOTE: no easy way to defer setting video_length
47 asset->video_length = stream->frame_count;
49 asset->width = stream->get_width();
50 asset->height = stream->get_height();
51 if (asset->width * asset->height <= 0)
53 eprintf("illegal frame size '%d x %d'\n", asset->width, asset->height);
58 asset->video_data = 1;
59 asset->audio_data = 0;
61 asset->frame_rate = stream->get_frame_rate();
62 asset->aspect_ratio = stream->get_aspect_ratio();
63 asset->interlace_mode = stream->get_interlace();
69 if (asset->use_pipe) {
70 result = stream->open_write(asset->path, asset->pipe);
72 result = stream->open_write(asset->path, NULL);
74 if (result) return result;
76 // not sure if we're supposed to send interlace info with each set of frames, (wouldn't know howto!)??
77 stream->set_interlace(asset->interlace_mode);
78 stream->set_width(asset->width);
79 stream->set_height(asset->height);
80 stream->set_frame_rate(asset->frame_rate);
81 stream->set_aspect_ratio(asset->aspect_ratio);
83 result = stream->write_header();
84 if (result) return result;
93 int FileYUV::close_file() {
94 if (pipe_latency && ffmpeg && stream) {
95 // deal with last frame still in the pipe
96 ensure_temp(incoming_asset->width, incoming_asset->height);
97 if (ffmpeg->decode(NULL, 0, temp) == 0)
100 yuv[0] = temp->get_y();
101 yuv[1] = temp->get_u();
102 yuv[2] = temp->get_v();
103 stream->write_frame(yuv);
108 if (ffmpeg) delete ffmpeg;
113 // NOTE: set_video_position() called every time a frame is read
114 int FileYUV::set_video_position(int64_t frame_number) {
115 return stream->seek_frame(frame_number);
118 int FileYUV::read_frame(VFrame *frame)
121 VFrame *input = frame;
123 // short cut for direct copy routines
124 if (frame->get_color_model() == BC_COMPRESSED) {
125 long frame_size = (long) // w*h + w*h/4 + w*h/4
126 (stream->get_height() * stream->get_width() * 1.5);
127 frame->allocate_compressed_data(frame_size);
128 frame->set_compressed_size(frame_size);
129 return stream->read_frame_raw(frame->get_data(), frame_size);
133 // process through a temp frame if necessary
134 if (! cmodel_is_planar(frame->get_color_model()) ||
135 (frame->get_w() != stream->get_width()) ||
136 (frame->get_h() != stream->get_height()))
138 ensure_temp(stream->get_width(), stream->get_height());
143 yuv[0] = input->get_y();
144 yuv[1] = input->get_u();
145 yuv[2] = input->get_v();
146 result = stream->read_frame(yuv);
147 if (result) return result;
149 // transfer from the temp frame to the real one
152 FFMPEG::convert_cmodel(input, frame);
158 int FileYUV::write_frames(VFrame ***layers, int len)
162 // only one layer supported
163 VFrame **frames = layers[0];
166 for (int n = 0; n < len; n++)
170 // short cut for direct copy routines
171 if (frame->get_color_model() == BC_COMPRESSED)
173 long frame_size = frame->get_compressed_size();
174 if (incoming_asset->format == FILE_YUV)
175 return stream->write_frame_raw(frame->get_data(), frame_size);
177 // decode and write an encoded frame
178 if (FFMPEG::codec_id(incoming_asset->vcodec) != CODEC_ID_NONE)
182 ffmpeg = new FFMPEG(incoming_asset);
183 ffmpeg->init(incoming_asset->vcodec);
186 ensure_temp(incoming_asset->width, incoming_asset->height);
187 int result = ffmpeg->decode(frame->get_data(), frame_size, temp);
189 // some formats are decoded one frame later
190 if (result == FFMPEG_LATENCY)
192 // remember to write the last frame
206 yuv[0] = temp->get_y();
207 yuv[1] = temp->get_u();
208 yuv[2] = temp->get_v();
209 return stream->write_frame(yuv);
213 // process through a temp frame only if necessary
214 if (! cmodel_is_planar(frame->get_color_model()) ||
215 (frame->get_w() != stream->get_width()) ||
216 (frame->get_h() != stream->get_height()))
218 ensure_temp(asset->width, asset->height);
219 FFMPEG::convert_cmodel(frame, temp);
224 yuv[0] = frame->get_y();
225 yuv[1] = frame->get_u();
226 yuv[2] = frame->get_v();
227 result = stream->write_frame(yuv);
228 if (result) return result;
235 void FileYUV::get_parameters(BC_WindowBase *parent_window,
237 BC_WindowBase* &format_window,
241 if (! video_options) return;
243 YUVConfigVideo *config = new YUVConfigVideo(parent_window, asset, format);
244 format_window = config;
245 config->create_objects();
246 if (config->run_window() == 0)
248 // save the new path and pipe to the asset
249 strcpy(asset->path, config->path_textbox->get_text());
250 strcpy(asset->pipe, config->pipe_textbox->get_text());
251 // are we using the pipe (if there is one)
252 asset->use_pipe = config->pipe_checkbox->get_value();
253 // update the path textbox in the render window
254 format->path_textbox->update(asset->path);
255 // add the new path and pipe to the defaults list
256 const char *prefix = FILE_FORMAT_PREFIX(asset->format);
257 config->path_recent->add_item(prefix, asset->path);
258 config->pipe_recent->add_item(prefix, asset->pipe);
263 int FileYUV::check_sig(Asset *asset)
266 FILE *f = fopen(asset->path, "rb");
268 // check for starting with "YUV4MPEG2"
269 fread(&temp, 9, 1, f);
271 if (strncmp(temp, "YUV4MPEG2", 9) == 0) return 1;
276 // NOTE: this is called on the write stream, not the read stream!
277 // as such, I have no idea what one is supposed to do with position.
278 int FileYUV::can_copy_from(Edit *edit, int64_t position)
280 // NOTE: width and height already checked in file.C
282 // FUTURE: is the incoming asset already available somewhere?
283 incoming_asset = edit->asset;
285 if (edit->asset->format == FILE_YUV) return 1;
287 // if FFMPEG can decode it, we'll accept it
288 if (FFMPEG::codec_id(edit->asset->vcodec) != CODEC_ID_NONE) return 1;
295 int FileYUV::get_best_colormodel(Asset *asset, int driver)
297 // FUTURE: is there a reason to try to accept anything else?
302 int FileYUV::colormodel_supported(int color_model)
304 // we convert internally to any color model proposed
306 // NOTE: file.C does not convert from YUV, so we have to do it.
311 Other member functions used in other file* modules:
313 write_compressed_frame(): used for record, so probably not needed
314 read_compressed_frame(): perhaps never used?
315 get_video_position: used by record only
316 reset_parameters(): not sure when used or needed
317 reset_parameters_derived(): not sure when used or needed
318 *_audio_*: yuv4mpeg doesn't handle audio
324 void FileYUV::ensure_temp(int width, int height)
326 // make sure the temp is correct size and type
327 if (temp && (temp->get_w() != width ||
328 temp->get_h() != height ||
329 temp->get_color_model() != BC_YUV420P))
335 // create a correct temp frame if we don't have one
338 temp = new VFrame(0, width, height, BC_YUV420P);
343 YUVConfigVideo::YUVConfigVideo(BC_WindowBase *parent_window, Asset *asset, FormatTools *format)
344 : BC_Window(PROGRAM_NAME ": YUV4MPEG Stream",
345 parent_window->get_abs_cursor_x(1),
346 parent_window->get_abs_cursor_y(1),
350 this->parent_window = parent_window;
352 this->format = format;
353 this->defaults = format->mwindow->defaults;
356 YUVConfigVideo::~YUVConfigVideo()
360 delete pipe_checkbox;
367 int YUVConfigVideo::create_objects()
376 add_subwindow(new BC_Title(init_x, y, _("Output Path:")));
377 add_subwindow(path_textbox = new BC_TextBox(init_x + 100, y, 350, 1, asset->path));
378 add_subwindow(path_recent = new BC_RecentList("PATH", defaults, path_textbox, 10, init_x + 450, y, path_textbox->get_w(), 100));
379 path_recent->load_items(FILE_FORMAT_PREFIX(asset->format));
384 add_subwindow(bt = new BC_Title(init_x, y, _("Use Pipe:")));
385 add_subwindow(pipe_checkbox = new PipeCheckBox(init_x + bt->get_w(), y, asset->use_pipe));
386 add_subwindow(pipe_textbox = new BC_TextBox(init_x + 100, y, 350, 1, asset->pipe));
387 add_subwindow(pipe_recent = new BC_RecentList("PIPE", defaults, pipe_textbox, 10, init_x + 450, y, pipe_textbox->get_w(), 100));
388 pipe_recent->load_items(FILE_FORMAT_PREFIX(asset->format));
390 pipe_checkbox->textbox = pipe_textbox;
391 if (!asset->use_pipe) pipe_textbox->disable();
395 add_subwindow(new BC_Title(x, y, _("Stream Header:"), MEDIUMFONT, RED));
399 add_subwindow(bt = new BC_Title(x, y, _("Interlacing:")));
400 char string[BCTEXTLEN];
401 ilacemode_to_text(string,asset->interlace_mode);
402 add_subwindow(new BC_Title(x + bt->get_w() + 5, y, string, MEDIUMFONT, YELLOW));
406 add_subwindow(new BC_Title(x, y, _("Pipe Presets:")));
409 add_subwindow(mpeg2enc = new PipePreset(x, y, "mpeg2enc", pipe_textbox, pipe_checkbox));
410 // NOTE: the '%' character will be replaced by the current path
411 // NOTE: to insert a real '%' double it up: '%%' -> '%'
412 // NOTE: preset items must have a '|' before the actual command
413 mpeg2enc->add_item(new BC_MenuItem ("(DVD) | mpeg2enc -f 8 -o %"));
414 mpeg2enc->add_item(new BC_MenuItem ("(VCD) | mpeg2enc -f 2 -o %"));
417 add_subwindow(ffmpeg = new PipePreset(x, y, "ffmpeg", pipe_textbox, pipe_checkbox));
418 ffmpeg->add_item(new BC_MenuItem("(DVD) | ffmpeg -f yuv4mpegpipe -i - -y -target dvd -ilme -ildct -hq -f mpeg2video %"));
419 ffmpeg->add_item(new BC_MenuItem("(VCD) | ffmpeg -f yuv4mpegpipe -i - -y -target vcd -hq -f mpeg2video %"));
421 add_subwindow(new BC_OKButton(this));
422 add_subwindow(new BC_CancelButton(this));
427 int YUVConfigVideo::close_event()
434 PipeCheckBox::PipeCheckBox(int x, int y, int value)
435 : BC_CheckBox(x, y, value)
440 int PipeCheckBox::handle_event()
450 PipePreset::PipePreset(int x, int y, char *title, BC_TextBox *textbox, BC_CheckBox *checkbox)
451 : BC_PopupMenu(x, y, 150, title)
453 this->pipe_textbox = textbox;
454 this->pipe_checkbox =checkbox;
458 int PipePreset::handle_event()
460 char *text = get_text();
461 // NOTE: preset items must have a '|' before the actual command
462 char *pipe = strchr(text, '|');
463 // pipe + 1 to skip over the '|'
464 if (pipe) pipe_textbox->update(pipe + 1);
466 pipe_textbox->enable();
467 pipe_checkbox->set_value(1, 1);
469 // menuitem sets the title after selection but we reset it