r870: Merge 2.1:
[cinelerra_cv.git] / cinelerra / fileyuv.C
blobbbdecf08f0f7f8acb96dcbddce43597178d5cd07
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"
11 #include <ctype.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <errno.h>
17 FileYUV::FileYUV(Asset *asset, File *file)
18         : FileBase(asset, file)
20         if (asset->format == FILE_UNKNOWN) asset->format = FILE_YUV;
21         asset->byte_order = 0; // FUTURE: is this always correct?
22         temp = 0;
23         ffmpeg = 0;
24         stream = new YUVStream();
25         pipe_latency = 0;
28 FileYUV::~FileYUV()
30         // NOTE: close_file() is already called
31         delete stream;
33         
34 int FileYUV::open_file(int should_read, int should_write)
36         int result;
38         if (should_read) {
40                 result = stream->open_read(asset->path);
41                 if (result) return result;
43                 // NOTE: no easy way to defer setting video_length
44                 asset->video_length = stream->frame_count;
45                 
46                 asset->width = stream->get_width();
47                 asset->height = stream->get_height();
48                 if (asset->width * asset->height <= 0) {
49                         printf("illegal frame size '%d x %d'\n", 
50                                asset->width, asset->height);
51                         return 1;
52                 }
53                 
54                 asset->layers = 1;
55                 asset->video_data = 1;
56                 asset->audio_data = 0;
57                 
58                 asset->frame_rate = stream->get_frame_rate();
59                 asset->aspect_ratio = stream->get_aspect_ratio();
60                 asset->interlace_mode = stream->get_interlace();
61                 
62                 return 0;
63         }
65         if (should_write) {
66                 if (asset->use_pipe) {
67                         result = stream->open_write(asset->path, asset->pipe);
68                 }
69                 else {
70                         result = stream->open_write(asset->path, NULL);
71                 }
72                 if (result) return result;
74                 // not sure if we're supposed to send interlace info with each set of frames, (wouldn't know howto!)??
75                 stream->set_interlace(asset->interlace_mode);
76                 stream->set_width(asset->width);
77                 stream->set_height(asset->height);
78                 stream->set_frame_rate(asset->frame_rate);
79                 stream->set_aspect_ratio(asset->aspect_ratio);
81                 result = stream->write_header();
82                 if (result) return result;
83                 
84                 return 0;
85         }
86         
87         // no action given
88         return 1;
91 int FileYUV::close_file() {
92         if (pipe_latency && ffmpeg && stream) {
93                 // deal with last frame still in the pipe
94                 ensure_temp(incoming_asset->width,
95                             incoming_asset->height); 
96                 if (ffmpeg->decode(NULL, 0, temp) == 0) {
97                         uint8_t *yuv[3];
98                         yuv[0] = temp->get_y();
99                         yuv[1] = temp->get_u();
100                         yuv[2] = temp->get_v();
101                         stream->write_frame(yuv);
102                 }
103                 pipe_latency = 0;
104         }
105         stream->close_fd();
106         if (ffmpeg) delete ffmpeg;
107         ffmpeg = 0;
108         return 0;
111 // NOTE: set_video_position() called every time a frame is read
112 int FileYUV::set_video_position(int64_t frame_number) {
113         return stream->seek_frame(frame_number);
116 int FileYUV::read_frame(VFrame *frame)
118         int result;
119         VFrame *input = frame;
121         // short cut for direct copy routines
122         if (frame->get_color_model() == BC_COMPRESSED) {
123                 long frame_size = (long) // w*h + w*h/4 + w*h/4
124                         (stream->get_height() * stream->get_width() * 1.5); 
125                 frame->allocate_compressed_data(frame_size);
126                 frame->set_compressed_size(frame_size);
127                 return stream->read_frame_raw(frame->get_data(), frame_size);
128         }
129         
131         // process through a temp frame if necessary
132         if (! cmodel_is_planar(frame->get_color_model()) ||
133             (frame->get_w() != stream->get_width()) ||
134             (frame->get_h() != stream->get_height())) {
135                 ensure_temp(stream->get_width(),
136                             stream->get_height());
137                 input = temp;
138         }
140         uint8_t *yuv[3];
141         yuv[0] = input->get_y();
142         yuv[1] = input->get_u();
143         yuv[2] = input->get_v();
144         result = stream->read_frame(yuv);
145         if (result) return result;
147         // transfer from the temp frame to the real one
148         if (input != frame) {
149                 FFMPEG::convert_cmodel(input, frame);
150         }
151         
152         return 0;
155 int FileYUV::write_frames(VFrame ***layers, int len)
157         int result;
159         // only one layer supported
160         VFrame **frames = layers[0];
161         VFrame *frame;
163         for (int n = 0; n < len; n++) {
165                 frame = frames[n];
167                 // short cut for direct copy routines
168                 if (frame->get_color_model() == BC_COMPRESSED) {
169                         long frame_size = frame->get_compressed_size();
170                         if (incoming_asset->format == FILE_YUV) {
171                                 return stream->write_frame_raw
172                                         (frame->get_data(), frame_size);
173                         }
175                         // decode and write an encoded frame
176                         if (FFMPEG::codec_id(incoming_asset->vcodec) != CODEC_ID_NONE) {
177                                 if (! ffmpeg) {
178                                         ffmpeg = new FFMPEG(incoming_asset);
179                                         ffmpeg->init(incoming_asset->vcodec);
180                                 }
181                                 
182                                 ensure_temp(incoming_asset->width,
183                                             incoming_asset->height); 
184                                 int result = ffmpeg->decode(frame->get_data(),
185                                                             frame_size, temp);
187                                 // some formats are decoded one frame later
188                                 if (result == FFMPEG_LATENCY) {
189                                         // remember to write the last frame
190                                         pipe_latency++;
191                                         return 0;
192                                 }
194                                 if (result) {
195                                         delete ffmpeg;
196                                         ffmpeg = 0;
197                                         return 1;
198                                 }
201                                 uint8_t *yuv[3];
202                                 yuv[0] = temp->get_y();
203                                 yuv[1] = temp->get_u();
204                                 yuv[2] = temp->get_v();
205                                 return stream->write_frame(yuv);
206                         }
208                 }
210                 // process through a temp frame only if necessary
211                 if (! cmodel_is_planar(frame->get_color_model()) ||
212                     (frame->get_w() != stream->get_width()) ||
213                     (frame->get_h() != stream->get_height())) {
214                         ensure_temp(asset->width, asset->height);
215                         FFMPEG::convert_cmodel(frame, temp);
216                         frame = temp;
217                 }
219                 uint8_t *yuv[3];
220                 yuv[0] = frame->get_y();
221                 yuv[1] = frame->get_u();
222                 yuv[2] = frame->get_v();
223                 result = stream->write_frame(yuv);
224                 if (result) return result;
227         }
229         return 0;
233 void FileYUV::get_parameters(BC_WindowBase *parent_window, 
234                              Asset *asset, 
235                              BC_WindowBase* &format_window,
236                              int video_options,
237                              FormatTools *format)
239         if (! video_options) return;
241         YUVConfigVideo *config =  
242                 new YUVConfigVideo(parent_window, asset, format);
243         format_window = config;
244         config->create_objects();
245         if (config->run_window() == 0) {
246                 // save the new path and pipe to the asset
247                 strcpy(asset->path, config->path_textbox->get_text());
248                 strcpy(asset->pipe, config->pipe_config->textbox->get_text());
249                 // are we using the pipe (if there is one)
250                 asset->use_pipe = config->pipe_config->checkbox->get_value();
251                 // update the path textbox in the render window
252                 format->path_textbox->update(asset->path);
253                 // set the pipe status in the render window
254                 format->pipe_status->set_status(asset);
255                 // and 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_config->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)
279 {       // NOTE: width and height already checked in file.C
281         // FUTURE: is the incoming asset already available somewhere?
282         incoming_asset = edit->asset;
284         if (edit->asset->format == FILE_YUV) return 1;
286         // if FFMPEG can decode it, we'll accept it
287         if (FFMPEG::codec_id(edit->asset->vcodec) != CODEC_ID_NONE) {
288                 return 1;
289         }
291         incoming_asset = 0;
293         return 0;
296 int FileYUV::get_best_colormodel(Asset *asset, int driver) 
298         // FUTURE: is there a reason to try to accept anything else?  
299         return BC_YUV420P;
303 int FileYUV::colormodel_supported(int color_model) 
305         // we convert internally to any color model proposed
306         return color_model;
307         // NOTE: file.C does not convert from YUV, so we have to do it.  
311 /*  
312     Other member functions used in other file* modules:
314     write_compressed_frame(): used for record, so probably not needed
315     read_compressed_frame(): perhaps never used?
316     get_video_position: used by record only
317     reset_parameters(): not sure when used or needed
318     reset_parameters_derived(): not sure when used or needed
319     *_audio_*: yuv4mpeg doesn't handle audio
320     
322         
325 void FileYUV::ensure_temp(int width, int height) {
326         
327         // make sure the temp is correct size and type
328         if (temp && (temp->get_w() != width ||
329                      temp->get_h() != height ||
330                      temp->get_color_model() != BC_YUV420P)) {
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                 temp = new VFrame(0, width, height, BC_YUV420P);
338         }
342 YUVConfigVideo::YUVConfigVideo(BC_WindowBase *parent_window, Asset *asset, 
343                                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_config;
361         delete mpeg2enc;
362         delete ffmpeg;
365 int YUVConfigVideo::create_objects()
367         int init_x = 10;
368         int init_y = 10;
369         
370         int x = init_x;
371         int y = init_y;
373         add_subwindow(new BC_Title(x, y, _("Output Path:")));
374         x += 120;
375         path_textbox = new BC_TextBox(x, y, 350, 1, asset->path);
376         add_subwindow(path_textbox);
378         x += 350;
379         path_recent = new BC_RecentList("PATH", defaults, path_textbox, 
380                                         10, x, y, 350, 100);
381         add_subwindow(path_recent);
382         path_recent->load_items(FILE_FORMAT_PREFIX(asset->format));
384         x = init_x;
385         y += 30;
387         pipe_config = new PipeConfig(this, defaults, asset);
388         pipe_config->create_objects(x, y, 350, asset->format);
389         
391         x = init_x;
392         y += 120;
394         add_subwindow(new BC_Title(x, y, _("Pipe Presets:")));
395         x += 130;
396         mpeg2enc = new PipePreset(x, y, "mpeg2enc", pipe_config);
397         add_subwindow(mpeg2enc);
398         // NOTE: the '%' character will be replaced by the current path
399         // NOTE: to insert a real '%' double it up: '%%' -> '%'
400         // NOTE: preset items must have a '|' before the actual command
401         mpeg2enc->add_item(new BC_MenuItem ("(DVD) | mpeg2enc -f 8 -o %"));
402         mpeg2enc->add_item(new BC_MenuItem ("(VCD) | mpeg2enc -f 2 -o %"));
404         x += 160;
405         ffmpeg = new PipePreset(x, y, "ffmpeg", pipe_config);
406         add_subwindow(ffmpeg);
407         ffmpeg->add_item(new BC_MenuItem("(DVD) | ffmpeg -f yuv4mpegpipe -i - -y -target dvd -ilme -ildct -hq -f mpeg2video %"));
408         ffmpeg->add_item(new BC_MenuItem("(VCD) | ffmpeg -f yuv4mpegpipe -i - -y -target vcd -hq -f mpeg2video %"));
410         add_subwindow(new BC_OKButton(this));
411         add_subwindow(new BC_CancelButton(this));
412         show_window();
413         return 0;
416 int YUVConfigVideo::close_event()
418         set_done(0);
419         return 1;