8 #include "interlacemodes.h"
11 YUVStream::YUVStream() {
12 y4m_init_stream_info(&stream_info);
13 y4m_init_frame_info(&frame_info);
20 YUVStream::~YUVStream() {
21 y4m_fini_stream_info(&stream_info);
22 y4m_fini_frame_info(&frame_info);
23 if (frame_index) delete frame_index;
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);
33 eprintf("Error while opening \"%s\" for reading. \n%m\n", path);
37 int result = read_header();
38 if (result != Y4M_OK) {
39 eprintf("Bad YUV4MPEG2 header: %s\n", y4m_strerr(result));
43 // generate index to frame position if not done yet
44 if (frame_index == 0) {
45 if (make_index() != 0) {
53 // NOTE: path is opened as a pipe if contains '|'
54 int YUVStream::open_write(char *path, char *pipe) {
56 // skip over the '|' if present
57 if (char *p = strchr(path, '|')) {
61 stream_pipe = new Pipe(pipe, path);
62 if (stream_pipe->open_write() == 0) {
63 stream_fd = stream_pipe->fd;
69 stream_fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0666);
70 if (stream_fd > 0) return 0;
72 eprintf("Error while opening \"%s\" for writing. \n%m\n", path);
77 void YUVStream::close_fd() {
84 if (stream_fd >= 0) close(stream_fd);
88 int YUVStream::read_frame(uint8_t *yuv[3]) {
89 int result = y4m_read_frame(stream_fd, &stream_info,
91 if (result != Y4M_OK) {
92 if (result != Y4M_ERR_EOF) {
93 eprintf("read_frame() failed: %s\n",
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);
106 int result = y4m_read_frame_header(stream_fd, &frame_info);
108 if (result != Y4M_OK) {
109 eprintf("y4m_read_frame_header() failed: %s\n",
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));
122 int YUVStream::write_frame(uint8_t *yuv[3]) {
123 int result = y4m_write_frame(stream_fd, &stream_info,
125 if (result != Y4M_OK) {
126 eprintf("write_frame() failed: %s\n", y4m_strerr(result));
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);
135 int result = y4m_write_frame_header(stream_fd, &frame_info);
137 if (result != Y4M_OK) {
138 eprintf("y4m_write_frame_header() failed: %s\n",
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));
151 int YUVStream::make_index() {
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
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);
182 // rewind to the start of the first frame
183 lseek(stream_fd, frame_index->values[0], SEEK_SET);
190 int YUVStream::seek_frame(int64_t frame_number) {
191 if (frame_number > frame_count ||
194 eprintf("seek_frame(%d) failed (frame_count=%d)\n",
195 frame_number, frame_count);
199 off_t position = frame_index->values[frame_number];
201 // because of header, position should never be zero
202 eprintf("seek_frame(%d): position was zero\n", frame_number);
205 if (lseek(stream_fd, position, SEEK_SET) < 0) {
206 eprintf("lseek(%d) failed: %s\n", position, strerror(errno));
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",
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",
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;
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,
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;
276 void YUVStream::set_aspect_ratio(double aspect_ratio) {
278 ratio.n = (int)(aspect_ratio * 10000);
280 y4m_ratio_t sar = y4m_guess_sar(get_width(), get_height(),
282 y4m_si_set_sampleaspect(&stream_info, sar);