r1006: configure: Use libx264_pic instead of libx264 if available.
[cinelerra_cv/mob.git] / cinelerra / fileyuv.C
bloba255e183cb1011c5dcc7b5638e6007687b319dea
1 #include "fileyuv.h"
2 #include "asset.h"
3 #include "bchash.h"
4 #include "edit.h"
5 #include "file.h"
6 #include "guicast.h"
7 #include "guicast.h"
8 #include "interlacemodes.h"
9 #include "quicktime.h"
10 #include "mainerror.h"
11 #include "mwindow.h"
12 #include "vframe.h"
14 #include <ctype.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <stdlib.h>
18 #include <string.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?
25         temp = 0;
26         ffmpeg = 0;
27         stream = new YUVStream();
28         pipe_latency = 0;
31 FileYUV::~FileYUV()
33         // NOTE: close_file() is already called
34         delete stream;
36         
37 int FileYUV::open_file(int should_read, int should_write)
39         int result;
41         if (should_read) 
42         {
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;
48                 
49                 asset->width = stream->get_width();
50                 asset->height = stream->get_height();
51                 if (asset->width * asset->height <= 0) 
52                 {
53                         eprintf("illegal frame size '%d x %d'\n", asset->width, asset->height);
54                         return 1;
55                 }
56                 
57                 asset->layers = 1;
58                 asset->video_data = 1;
59                 asset->audio_data = 0;
60                 
61                 asset->frame_rate = stream->get_frame_rate();
62                 asset->aspect_ratio = stream->get_aspect_ratio();
63                 asset->interlace_mode = stream->get_interlace();
64                 
65                 return 0;
66         }
68         if (should_write) {
69                 if (asset->use_pipe) {
70                         result = stream->open_write(asset->path, asset->pipe);
71                 } else {
72                         result = stream->open_write(asset->path, NULL);
73                 }
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;
85                 
86                 return 0;
87         }
88         
89         // no action given
90         return 1;
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) 
98                 {
99                         uint8_t *yuv[3];
100                         yuv[0] = temp->get_y();
101                         yuv[1] = temp->get_u();
102                         yuv[2] = temp->get_v();
103                         stream->write_frame(yuv);
104                 }
105                 pipe_latency = 0;
106         }
107         stream->close_fd();
108         if (ffmpeg) delete ffmpeg;
109         ffmpeg = 0;
110         return 0;
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)
120         int result;
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);
130         }
131         
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())) 
137         {
138                 ensure_temp(stream->get_width(), stream->get_height());
139                 input = temp;
140         }
142         uint8_t *yuv[3];
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
150         if (input != frame) 
151         {
152                 FFMPEG::convert_cmodel(input, frame);
153         }
154         
155         return 0;
158 int FileYUV::write_frames(VFrame ***layers, int len)
160         int result;
162         // only one layer supported
163         VFrame **frames = layers[0];
164         VFrame *frame;
166         for (int n = 0; n < len; n++) 
167         {
168                 frame = frames[n];
170                 // short cut for direct copy routines
171                 if (frame->get_color_model() == BC_COMPRESSED) 
172                 {
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) 
179                         {
180                                 if (! ffmpeg) 
181                                 {
182                                         ffmpeg = new FFMPEG(incoming_asset);
183                                         ffmpeg->init(incoming_asset->vcodec);
184                                 }
185                                 
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) 
191                                 {
192                                         // remember to write the last frame
193                                         pipe_latency++;
194                                         return 0;
195                                 }
197                                 if (result) 
198                                 {
199                                         delete ffmpeg;
200                                         ffmpeg = 0;
201                                         return 1;
202                                 }
205                                 uint8_t *yuv[3];
206                                 yuv[0] = temp->get_y();
207                                 yuv[1] = temp->get_u();
208                                 yuv[2] = temp->get_v();
209                                 return stream->write_frame(yuv);
210                         }
211                 }
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())) 
217                 {
218                         ensure_temp(asset->width, asset->height);
219                         FFMPEG::convert_cmodel(frame, temp);
220                         frame = temp;
221                 }
223                 uint8_t *yuv[3];
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;
229         }
231         return 0;
235 void FileYUV::get_parameters(BC_WindowBase *parent_window, 
236                              Asset *asset, 
237                              BC_WindowBase* &format_window,
238                              int video_options,
239                              FormatTools *format)
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) 
247         {
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);
259         }
260         delete config;
263 int FileYUV::check_sig(Asset *asset)
265         char temp[9];
266         FILE *f = fopen(asset->path, "rb");
268         // check for starting with "YUV4MPEG2"
269         fread(&temp, 9, 1, f);
270         fclose(f);
271         if (strncmp(temp, "YUV4MPEG2", 9) == 0) return 1;
273         return 0;
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;
290         incoming_asset = 0;
292         return 0;
295 int FileYUV::get_best_colormodel(Asset *asset, int driver) 
297         // FUTURE: is there a reason to try to accept anything else?  
298         return BC_YUV420P;
302 int FileYUV::colormodel_supported(int color_model) 
304         // we convert internally to any color model proposed
305         return color_model;
306         // NOTE: file.C does not convert from YUV, so we have to do it.  
310 /*  
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
319     
321         
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)) 
330         {
331                 delete temp;
332                 temp = 0;
333         }
334         
335         // create a correct temp frame if we don't have one
336         if (temp == 0) 
337         {
338                 temp = new VFrame(0, width, height, BC_YUV420P);
339         }
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),
347                     500,
348                     240)
350         this->parent_window = parent_window;
351         this->asset = asset;
352         this->format = format;
353         this->defaults = format->mwindow->defaults;
356 YUVConfigVideo::~YUVConfigVideo()
358         delete path_textbox;
359         delete path_recent;
360         delete pipe_checkbox;
361         delete pipe_textbox;
362         delete pipe_recent;
363         delete mpeg2enc;
364         delete ffmpeg;
367 int YUVConfigVideo::create_objects()
369         BC_Title *bt;
370         int init_x = 10;
371         int init_y = 10;
372         
373         int x = init_x;
374         int y = init_y;
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));
381         x = init_x;
382         y += 30;
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();
393         x = init_x;
394         y += 30;
395         add_subwindow(new BC_Title(x, y, _("Stream Header:"), MEDIUMFONT, RED));
397         x = init_x + 20;
398         y += 30;
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));
404         x = init_x;
405         y += 30;
406         add_subwindow(new BC_Title(x, y, _("Pipe Presets:")));
408         x += 130;
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 %"));
416         x += 180;
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));
423         show_window();
424         return 0;
427 int YUVConfigVideo::close_event()
429         set_done(0);
430         return 1;
434 PipeCheckBox::PipeCheckBox(int x, int y, int value)
435         : BC_CheckBox(x, y, value)
437         this->textbox = 0;
440 int PipeCheckBox::handle_event() 
442         if (textbox)
443                 if (get_value()) 
444                         textbox->enable();
445                 else 
446                         textbox->disable();
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;
455         this->title = title;
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);
468         
469         // menuitem sets the title after selection but we reset it
470         set_text(title);