r1009: Move the dependencies to newer package names
[cinelerra_cv/mob.git] / cinelerra / yuvstream.C
blob53617f7fcacfb6657d589b43fe8ebac1f6015fa6
1 #include <fcntl.h>
2 #include <string.h>
3 #include <errno.h>
5 #include "guicast.h"
6 #include "pipe.h"
7 #include "yuvstream.h"
8 #include "interlacemodes.h"
9 #include "mainerror.h"
11 YUVStream::YUVStream() { 
12         y4m_init_stream_info(&stream_info);
13         y4m_init_frame_info(&frame_info);
14         stream_fd = -1;
15         stream_pipe= 0;
16         frame_count = 0;
17         frame_index = 0;
20 YUVStream::~YUVStream() {               
21         y4m_fini_stream_info(&stream_info);
22         y4m_fini_frame_info(&frame_info);
23         if (frame_index) delete frame_index;
24         close_fd();
28 int YUVStream::open_read(char *path) {
29         // NOTE: reading from pipes would be very difficult without temp files
30         stream_fd = open(path, O_RDONLY);
32         if (stream_fd < 0) {
33                 eprintf("Error while opening \"%s\" for reading. \n%m\n", path);
34                 return 1;
35         }
37         int result = read_header();
38         if (result != Y4M_OK) {
39                 eprintf("Bad YUV4MPEG2 header: %s\n", y4m_strerr(result));
40                 return 1;
41         }
43         // generate index to frame position if not done yet
44         if (frame_index == 0) {
45                 if (make_index() != 0) {
46                         return 1;
47                 }
48         }
50         return 0;
53 // NOTE: path is opened as a pipe if contains '|'
54 int YUVStream::open_write(char *path, char *pipe) {
55         if (pipe && *pipe) {
56                 // skip over the '|' if present
57                 if (char *p = strchr(path, '|')) {
58                         pipe = p + 1;
59                 }
61                 stream_pipe = new Pipe(pipe, path);
62                 if (stream_pipe->open_write() == 0) {
63                         stream_fd = stream_pipe->fd;
64                         return 0;
65                 }
66                 return 1;
67         }
69         stream_fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0666);
70         if (stream_fd > 0) return 0;
71         
72         eprintf("Error while opening \"%s\" for writing. \n%m\n", path);
73         return 1;
77 void YUVStream::close_fd() {
78         if (stream_pipe) {
79                 stream_pipe->close();
80                 stream_pipe = 0;
81                 stream_fd = -1;
82         }
83                 
84         if (stream_fd >= 0) close(stream_fd);
85         stream_fd = -1;
88 int YUVStream::read_frame(uint8_t *yuv[3]) {
89         int result =  y4m_read_frame(stream_fd, &stream_info, 
90                                   &frame_info, yuv);
91         if (result != Y4M_OK) {
92                 if (result != Y4M_ERR_EOF) {
93                         eprintf("read_frame() failed: %s\n", 
94                                y4m_strerr(result));
95                 }
96                 return 1;
97         }
99         return 0;
102 int YUVStream::read_frame_raw(uint8_t *data, long frame_size) {
103 #if (MJPEGTOOLS_Y4M_WRITE_FRAME_HEADER__3ARGS)
104         int result = y4m_read_frame_header(stream_fd, &stream_info, &frame_info);
105 #else
106         int result = y4m_read_frame_header(stream_fd, &frame_info);
107 #endif
108         if (result != Y4M_OK) {
109                 eprintf("y4m_read_frame_header() failed: %s\n", 
110                        y4m_strerr(result));
111                 return 1;
112         }
113         result = y4m_read(stream_fd, data, frame_size);
114         if (result != Y4M_OK) {
115                 printf("y4m_read(%d) failed: %s\n", 
116                        frame_size, y4m_strerr(result));
117                 return 1;
118         }
119         return 0;
121                 
122 int YUVStream::write_frame(uint8_t *yuv[3]) {
123         int result = y4m_write_frame(stream_fd, &stream_info, 
124                                      &frame_info, yuv);
125         if (result != Y4M_OK) {
126                 eprintf("write_frame() failed: %s\n", y4m_strerr(result));
127                 return 1;
128         }
129         return 0;
131 int YUVStream::write_frame_raw(uint8_t *data, long frame_size) {
132 #if (MJPEGTOOLS_Y4M_WRITE_FRAME_HEADER__3ARGS)
133         int result = y4m_write_frame_header(stream_fd, &stream_info, &frame_info);
134 #else
135         int result = y4m_write_frame_header(stream_fd, &frame_info);
136 #endif
137         if (result != Y4M_OK) {
138                 eprintf("y4m_write_frame_header() failed: %s\n", 
139                        y4m_strerr(result));
140                 return 1;
141         }
142         result = y4m_write(stream_fd, data, frame_size);
143         if (result != Y4M_OK) {
144                 eprintf("y4m_write(%d) failed: %s\n", 
145                        frame_size, y4m_strerr(result));
146                 return 1;
147         }
148         return 0;
149 }               
151 int YUVStream::make_index() {
152         off_t position;
153         uint8_t *yuv[3];
155         // NOTE: make_index() must be called after read_header().
157         // NOTE: storing frame_index locally means it is destroyed too often.
158         //       make_index() will be called 3 times per file.  If this
159         //       becomes a performance problem, the index should be cached.
160         //       Storing in 'asset' helps some, but still is done twice.
161         if (frame_index) delete frame_index;
162         frame_index = new ArrayList<off_t>;
164         VFrame *frame = new VFrame(0, get_width(), get_height(), BC_YUV420P);
165         yuv[0] = frame->get_y();
166         yuv[1] = frame->get_u();
167         yuv[2] = frame->get_v();
169         // note the start of the first frame
170         position = lseek(stream_fd, 0, SEEK_CUR);
172         // reset the frame count
173         frame_count = 0;
175         while (read_frame(yuv) == 0) {
176                 // index is start position of each frame
177                 frame_index->append(position);
178                 position = lseek(stream_fd, 0, SEEK_CUR);
179                 frame_count++;
180         } 
182         // rewind to the start of the first frame
183         lseek(stream_fd, frame_index->values[0], SEEK_SET);
185         delete frame;
187         return 0;
190 int YUVStream::seek_frame(int64_t frame_number) {
191         if (frame_number > frame_count ||
192             frame_number < 0 ||
193             frame_index == 0) {
194                 eprintf("seek_frame(%d) failed (frame_count=%d)\n", 
195                        frame_number, frame_count);
196                 return 1;
197         }
199         off_t position = frame_index->values[frame_number];
200         if (position == 0) {
201                 // because of header, position should never be zero
202                 eprintf("seek_frame(%d): position was zero\n", frame_number);
203         }
205         if (lseek(stream_fd, position, SEEK_SET) < 0) {
206                 eprintf("lseek(%d) failed: %s\n", position, strerror(errno));
207                 return 1;
208         }
210         return 0;
213 int YUVStream::read_header() {
214         int result = y4m_read_stream_header(stream_fd, &stream_info);
215         if (result != Y4M_OK) {
216                 eprintf("y4m_read_stream_header() failed: %s\n", 
217                        y4m_strerr(result));
218                 return 1;
219         }
220         return 0;
222 int YUVStream::write_header() {
223         int result = y4m_write_stream_header(stream_fd, &stream_info);
224         if (result != Y4M_OK) {
225                 eprintf("y4m_write_stream_header() failed: %s\n", 
226                        y4m_strerr(result));
227                 return 1;
228         }
229         return 0;
232 int YUVStream::get_interlace() {
233         return ilace_yuv4mpeg_to_bc(y4m_si_get_interlace(&stream_info));
236 void YUVStream::set_interlace(int imode) {
237         y4m_si_set_interlace(&stream_info, ilace_bc_to_yuv4mpeg(imode));
240 int YUVStream::get_width() {
241         return y4m_si_get_width(&stream_info);
243 void YUVStream::set_width(int width) {
244         y4m_si_set_width(&stream_info, width);
247 int YUVStream::get_height() {
248         return y4m_si_get_height(&stream_info);
250 void YUVStream::set_height(int height) {
251         y4m_si_set_height(&stream_info, height);
254 double YUVStream::get_frame_rate() {
255         y4m_ratio_t ratio = y4m_si_get_framerate(&stream_info);
256         double frame_rate = (double) ratio.n / (double) ratio.d;
257         return frame_rate;
259 void YUVStream::set_frame_rate(double frame_rate) {
260         y4m_ratio_t ratio = mpeg_conform_framerate(frame_rate);
261         y4m_si_set_framerate(&stream_info, ratio);
264 // FUTURE: these seem like a mess, possibly because I don't 
265 //         properly understand "display aspect" vs "pixel aspect"
266 double YUVStream::get_aspect_ratio() {
267         y4m_ratio_t sar = y4m_si_get_sampleaspect(&stream_info);
268         mpeg_aspect_code_t code = 
269                 mpeg_guess_mpeg_aspect_code(2, sar, 
270                                             get_width(),
271                                             get_height());
272         y4m_ratio_t aspect_ratio =  mpeg_framerate(code);
273         if (aspect_ratio.d == 0) return 0;
274         return (double) aspect_ratio.n / (double) aspect_ratio.d;
275 }               
276 void YUVStream::set_aspect_ratio(double aspect_ratio) {
277         y4m_ratio_t ratio;
278         ratio.n = (int)(aspect_ratio * 10000);
279         ratio.d = 10000;
280         y4m_ratio_t sar = y4m_guess_sar(get_width(), get_height(),
281                                         ratio);
282         y4m_si_set_sampleaspect(&stream_info, sar);