r956: README.BUILD - add more library recommendations
[cinelerra_cv/ct.git] / cinelerra / fileyuv.C
blobe33a3f8e1979e2e27b89fef93af73fb62b4a7d96
1 #include "fileyuv.h"
2 #include "asset.h"
3 #include "bchash.h"
4 #include "file.h"
5 #include "guicast.h"
6 #include "mwindow.h"
7 #include "vframe.h"
8 #include "edit.h"
9 #include "quicktime.h"
10 #include "mainerror.h"
12 #include <ctype.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <fcntl.h>
16 #include <errno.h>
18 FileYUV::FileYUV(Asset *asset, File *file)
19         : FileBase(asset, file)
21         if (asset->format == FILE_UNKNOWN) asset->format = FILE_YUV;
22         asset->byte_order = 0; // FUTURE: is this always correct?
23         temp = 0;
24         ffmpeg = 0;
25         stream = new YUVStream();
26         pipe_latency = 0;
29 FileYUV::~FileYUV()
31         // NOTE: close_file() is already called
32         delete stream;
34         
35 int FileYUV::open_file(int should_read, int should_write)
37         int result;
39         if (should_read) {
41                 result = stream->open_read(asset->path);
42                 if (result) return result;
44                 // NOTE: no easy way to defer setting video_length
45                 asset->video_length = stream->frame_count;
46                 
47                 asset->width = stream->get_width();
48                 asset->height = stream->get_height();
49                 if (asset->width * asset->height <= 0) {
50                         eprintf("illegal frame size '%d x %d'\n", 
51                                asset->width, asset->height);
52                         return 1;
53                 }
54                 
55                 asset->layers = 1;
56                 asset->video_data = 1;
57                 asset->audio_data = 0;
58                 
59                 asset->frame_rate = stream->get_frame_rate();
60                 asset->aspect_ratio = stream->get_aspect_ratio();
61                 asset->interlace_mode = stream->get_interlace();
62                 
63                 return 0;
64         }
66         if (should_write) {
67                 if (asset->use_pipe) {
68                         result = stream->open_write(asset->path, asset->pipe);
69                 }
70                 else {
71                         result = stream->open_write(asset->path, NULL);
72                 }
73                 if (result) return result;
75                 // not sure if we're supposed to send interlace info with each set of frames, (wouldn't know howto!)??
76                 stream->set_interlace(asset->interlace_mode);
77                 stream->set_width(asset->width);
78                 stream->set_height(asset->height);
79                 stream->set_frame_rate(asset->frame_rate);
80                 stream->set_aspect_ratio(asset->aspect_ratio);
82                 result = stream->write_header();
83                 if (result) return result;
84                 
85                 return 0;
86         }
87         
88         // no action given
89         return 1;
92 int FileYUV::close_file() {
93         if (pipe_latency && ffmpeg && stream) {
94                 // deal with last frame still in the pipe
95                 ensure_temp(incoming_asset->width,
96                             incoming_asset->height); 
97                 if (ffmpeg->decode(NULL, 0, temp) == 0) {
98                         uint8_t *yuv[3];
99                         yuv[0] = temp->get_y();
100                         yuv[1] = temp->get_u();
101                         yuv[2] = temp->get_v();
102                         stream->write_frame(yuv);
103                 }
104                 pipe_latency = 0;
105         }
106         stream->close_fd();
107         if (ffmpeg) delete ffmpeg;
108         ffmpeg = 0;
109         return 0;
112 // NOTE: set_video_position() called every time a frame is read
113 int FileYUV::set_video_position(int64_t frame_number) {
114         return stream->seek_frame(frame_number);
117 int FileYUV::read_frame(VFrame *frame)
119         int result;
120         VFrame *input = frame;
122         // short cut for direct copy routines
123         if (frame->get_color_model() == BC_COMPRESSED) {
124                 long frame_size = (long) // w*h + w*h/4 + w*h/4
125                         (stream->get_height() * stream->get_width() * 1.5); 
126                 frame->allocate_compressed_data(frame_size);
127                 frame->set_compressed_size(frame_size);
128                 return stream->read_frame_raw(frame->get_data(), frame_size);
129         }
130         
132         // process through a temp frame if necessary
133         if (! cmodel_is_planar(frame->get_color_model()) ||
134             (frame->get_w() != stream->get_width()) ||
135             (frame->get_h() != stream->get_height())) {
136                 ensure_temp(stream->get_width(),
137                             stream->get_height());
138                 input = temp;
139         }
141         uint8_t *yuv[3];
142         yuv[0] = input->get_y();
143         yuv[1] = input->get_u();
144         yuv[2] = input->get_v();
145         result = stream->read_frame(yuv);
146         if (result) return result;
148         // transfer from the temp frame to the real one
149         if (input != frame) {
150                 FFMPEG::convert_cmodel(input, frame);
151         }
152         
153         return 0;
156 int FileYUV::write_frames(VFrame ***layers, int len)
158         int result;
160         // only one layer supported
161         VFrame **frames = layers[0];
162         VFrame *frame;
164         for (int n = 0; n < len; n++) {
166                 frame = frames[n];
168                 // short cut for direct copy routines
169                 if (frame->get_color_model() == BC_COMPRESSED) {
170                         long frame_size = frame->get_compressed_size();
171                         if (incoming_asset->format == FILE_YUV) {
172                                 return stream->write_frame_raw
173                                         (frame->get_data(), frame_size);
174                         }
176                         // decode and write an encoded frame
177                         if (FFMPEG::codec_id(incoming_asset->vcodec) != CODEC_ID_NONE) {
178                                 if (! ffmpeg) {
179                                         ffmpeg = new FFMPEG(incoming_asset);
180                                         ffmpeg->init(incoming_asset->vcodec);
181                                 }
182                                 
183                                 ensure_temp(incoming_asset->width,
184                                             incoming_asset->height); 
185                                 int result = ffmpeg->decode(frame->get_data(),
186                                                             frame_size, temp);
188                                 // some formats are decoded one frame later
189                                 if (result == FFMPEG_LATENCY) {
190                                         // remember to write the last frame
191                                         pipe_latency++;
192                                         return 0;
193                                 }
195                                 if (result) {
196                                         delete ffmpeg;
197                                         ffmpeg = 0;
198                                         return 1;
199                                 }
202                                 uint8_t *yuv[3];
203                                 yuv[0] = temp->get_y();
204                                 yuv[1] = temp->get_u();
205                                 yuv[2] = temp->get_v();
206                                 return stream->write_frame(yuv);
207                         }
209                 }
211                 // process through a temp frame only if necessary
212                 if (! cmodel_is_planar(frame->get_color_model()) ||
213                     (frame->get_w() != stream->get_width()) ||
214                     (frame->get_h() != stream->get_height())) {
215                         ensure_temp(asset->width, asset->height);
216                         FFMPEG::convert_cmodel(frame, temp);
217                         frame = temp;
218                 }
220                 uint8_t *yuv[3];
221                 yuv[0] = frame->get_y();
222                 yuv[1] = frame->get_u();
223                 yuv[2] = frame->get_v();
224                 result = stream->write_frame(yuv);
225                 if (result) return result;
228         }
230         return 0;
234 void FileYUV::get_parameters(BC_WindowBase *parent_window, 
235                              Asset *asset, 
236                              BC_WindowBase* &format_window,
237                              int video_options,
238                              FormatTools *format)
240         if (! video_options) return;
242         YUVConfigVideo *config =  
243                 new YUVConfigVideo(parent_window, asset, format);
244         format_window = config;
245         config->create_objects();
246         if (config->run_window() == 0) {
247                 // save the new path and pipe to the asset
248                 strcpy(asset->path, config->path_textbox->get_text());
249                 strcpy(asset->pipe, config->pipe_config->textbox->get_text());
250                 // are we using the pipe (if there is one)
251                 asset->use_pipe = config->pipe_config->checkbox->get_value();
252                 // update the path textbox in the render window
253                 format->path_textbox->update(asset->path);
254                 // set the pipe status in the render window
255                 format->pipe_status->set_status(asset);
256                 // and add the new path and pipe to the defaults list
257                 const char *prefix = FILE_FORMAT_PREFIX(asset->format);
258                 config->path_recent->add_item(prefix, asset->path);
259                 config->pipe_config->recent->add_item(prefix, asset->pipe);
260         }
261         delete config;
264 int FileYUV::check_sig(Asset *asset)
266         char temp[9];
267         FILE *f = fopen(asset->path, "rb");
269         // check for starting with "YUV4MPEG2"
270         fread(&temp, 9, 1, f);
271         fclose(f);
272         if (strncmp(temp, "YUV4MPEG2", 9) == 0) return 1;
274         return 0;
277 // NOTE: this is called on the write stream, not the read stream!
278 //       as such, I have no idea what one is supposed to do with position.
279 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) {
289                 return 1;
290         }
292         incoming_asset = 0;
294         return 0;
297 int FileYUV::get_best_colormodel(Asset *asset, int driver) 
299         // FUTURE: is there a reason to try to accept anything else?  
300         return BC_YUV420P;
304 int FileYUV::colormodel_supported(int color_model) 
306         // we convert internally to any color model proposed
307         return color_model;
308         // NOTE: file.C does not convert from YUV, so we have to do it.  
312 /*  
313     Other member functions used in other file* modules:
315     write_compressed_frame(): used for record, so probably not needed
316     read_compressed_frame(): perhaps never used?
317     get_video_position: used by record only
318     reset_parameters(): not sure when used or needed
319     reset_parameters_derived(): not sure when used or needed
320     *_audio_*: yuv4mpeg doesn't handle audio
321     
323         
326 void FileYUV::ensure_temp(int width, int height) {
327         
328         // make sure the temp is correct size and type
329         if (temp && (temp->get_w() != width ||
330                      temp->get_h() != height ||
331                      temp->get_color_model() != BC_YUV420P)) {
332                 delete temp;
333                 temp = 0;
334         }
335         
336         // create a correct temp frame if we don't have one
337         if (temp == 0) {
338                 temp = new VFrame(0, width, height, BC_YUV420P);
339         }
343 YUVConfigVideo::YUVConfigVideo(BC_WindowBase *parent_window, Asset *asset, 
344                                FormatTools *format)
345         : BC_Window(PROGRAM_NAME ": YUV4MPEG Stream",
346                     parent_window->get_abs_cursor_x(1),
347                     parent_window->get_abs_cursor_y(1),
348                     500,
349                     240)
351         this->parent_window = parent_window;
352         this->asset = asset;
353         this->format = format;
354         this->defaults = format->mwindow->defaults;
357 YUVConfigVideo::~YUVConfigVideo()
359         delete path_textbox;
360         delete path_recent;
361         delete pipe_config;
362         delete mpeg2enc;
363         delete ffmpeg;
366 int YUVConfigVideo::create_objects()
368         int init_x = 10;
369         int init_y = 10;
370         
371         int x = init_x;
372         int y = init_y;
374         add_subwindow(new BC_Title(x, y, _("Output Path:")));
375         x += 120;
376         path_textbox = new BC_TextBox(x, y, 350, 1, asset->path);
377         add_subwindow(path_textbox);
379         x += 350;
380         path_recent = new BC_RecentList("PATH", defaults, path_textbox, 
381                                         10, x, y, 350, 100);
382         add_subwindow(path_recent);
383         path_recent->load_items(FILE_FORMAT_PREFIX(asset->format));
385         x = init_x;
386         y += 30;
388         pipe_config = new PipeConfig(this, defaults, asset);
389         pipe_config->create_objects(x, y, 350, asset->format);
390         
392         x = init_x;
393         y += 120;
395         add_subwindow(new BC_Title(x, y, _("Pipe Presets:")));
396         x += 130;
397         mpeg2enc = new PipePreset(x, y, "mpeg2enc", pipe_config);
398         add_subwindow(mpeg2enc);
399         // NOTE: the '%' character will be replaced by the current path
400         // NOTE: to insert a real '%' double it up: '%%' -> '%'
401         // NOTE: preset items must have a '|' before the actual command
402         mpeg2enc->add_item(new BC_MenuItem ("(DVD) | mpeg2enc -f 8 -o %"));
403         mpeg2enc->add_item(new BC_MenuItem ("(VCD) | mpeg2enc -f 2 -o %"));
405         x += 160;
406         ffmpeg = new PipePreset(x, y, "ffmpeg", pipe_config);
407         add_subwindow(ffmpeg);
408         ffmpeg->add_item(new BC_MenuItem("(DVD) | ffmpeg -f yuv4mpegpipe -i - -y -target dvd -ilme -ildct -hq -f mpeg2video %"));
409         ffmpeg->add_item(new BC_MenuItem("(VCD) | ffmpeg -f yuv4mpegpipe -i - -y -target vcd -hq -f mpeg2video %"));
411         add_subwindow(new BC_OKButton(this));
412         add_subwindow(new BC_CancelButton(this));
413         show_window();
414         return 0;
417 int YUVConfigVideo::close_event()
419         set_done(0);
420         return 1;