diff options
author | Stanimir Varbanov <stanimir.varbanov@linaro.org> | 2015-10-06 14:38:52 +0300 |
---|---|---|
committer | Stanimir Varbanov <stanimir.varbanov@linaro.org> | 2015-10-20 12:39:57 +0300 |
commit | 3b246cc0b6cb60102a7dd4a35d5f4d15ef541562 (patch) | |
tree | 4edc7b2ed49c90788f2268f16c3150e0fc6bff31 | |
parent | 83cd6c83aa6a75c7686e1300f8aecddea8f92d2f (diff) | |
download | v4l2-encode-3b246cc0b6cb60102a7dd4a35d5f4d15ef541562.tar.gz |
encoding h264 720p@30 - works
Signed-off-by: Stanimir Varbanov <stanimir.varbanov@linaro.org>
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | args.c | 7 | ||||
-rw-r--r-- | common.h | 25 | ||||
-rw-r--r-- | fileops.c | 2 | ||||
-rw-r--r-- | main.c | 240 | ||||
-rw-r--r-- | video.c | 79 |
6 files changed, 186 insertions, 173 deletions
@@ -34,10 +34,10 @@ INCLUDES = -I$(KERNELHEADERS) -I/usr/include/drm SOURCES = main.c fileops.c args.c video.c OBJECTS := $(SOURCES:.c=.o) -EXEC = v4l2_decode +EXEC = v4l2_encode CFLAGS = -Wall -g -std=gnu99 #-Os -LIBS = -lpthread -ldrm_freedreno -lm +LIBS = -lpthread -lm LIBPATH = -L/usr/lib/aarch64-linux-gnu LDFLAGS = -o $(EXEC) $(LIBPATH) $(LIBS) @@ -47,7 +47,7 @@ all: $(EXEC) $(CC) -c $(CFLAGS) $(INCLUDES) $< $(EXEC): $(OBJECTS) - $(CC) -o v4l2_decode $(OBJECTS) -lpthread -ldrm -ldrm_freedreno + $(CC) -o v4l2_encode $(OBJECTS) -lpthread clean: rm -f *.o $(EXEC) @@ -74,7 +74,7 @@ int parse_args(struct instance *i, int argc, char **argv) init_to_defaults(i); - while ((c = getopt(argc, argv, "w:h:c:di:m:f:")) != -1) { + while ((c = getopt(argc, argv, "w:h:c:di:m:f:n:")) != -1) { switch (c) { case 'c': i->codec = get_codec(optarg); @@ -95,9 +95,12 @@ int parse_args(struct instance *i, int argc, char **argv) i->height = atoi(optarg); break; case 'f': - i->save_frames = 1; + i->save_encoded = 1; i->save_path = optarg; break; + case 'n': + i->num_frames_to_save = atoi(optarg); + break; default: err("Bad argument"); return -1; @@ -25,6 +25,7 @@ #define INCLUDE_COMMON_H #include <stdio.h> +#include <stdint.h> #include <semaphore.h> /* When ADD_DETAILS is defined every debug and error message contains @@ -94,6 +95,15 @@ struct input { int offs; }; +/* Output file related parameters */ +struct output { + char *name; + int fd; + char *p; + unsigned int size; + off_t offs; +}; + /* video decoder related parameters */ struct video { char *name; @@ -102,8 +112,8 @@ struct video { /* Output queue related */ int out_buf_cnt; int out_buf_size; - int out_buf_off[MAX_OUT_BUF]; - char *out_buf_addr[MAX_OUT_BUF]; + uint32_t out_buf_off[MAX_OUT_BUF]; + uint8_t *out_buf_addr[MAX_OUT_BUF]; int out_buf_flag[MAX_OUT_BUF]; /* Capture queue related */ @@ -116,25 +126,28 @@ struct video { int cap_buf_cnt; int cap_buf_cnt_min; int cap_buf_size[CAP_PLANES]; - int cap_buf_off[MAX_CAP_BUF][CAP_PLANES]; - char *cap_buf_addr[MAX_CAP_BUF][CAP_PLANES]; + uint32_t cap_buf_off[MAX_CAP_BUF][CAP_PLANES]; + uint8_t *cap_buf_addr[MAX_CAP_BUF][CAP_PLANES]; int cap_buf_flag[MAX_CAP_BUF]; int cap_buf_queued; - unsigned long total_captured; + unsigned long total_encoded; }; struct instance { int width; int height; - int save_frames; + int save_encoded; char *save_path; + unsigned long num_frames_to_save; int use_dmabuf; /* Input file related parameters */ struct input in; + struct output out; + /* video decoder related parameters */ struct video video; @@ -25,6 +25,7 @@ #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <string.h> #include "common.h" #include "fileops.h" @@ -56,5 +57,6 @@ void input_close(struct instance *i) { munmap(i->in.p, i->in.size); close(i->in.fd); + memset(&i->in, 0, sizeof(i->in)); } @@ -33,6 +33,7 @@ #include <errno.h> #include <fcntl.h> #include <unistd.h> +#include <stdint.h> #include "args.h" #include "common.h" @@ -56,27 +57,6 @@ static const int event_type[] = { V4L2_EVENT_SOURCE_CHANGE, }; -static struct timeval start, end; - -static void time_start(void) -{ - gettimeofday(&start, NULL); -} - -static void print_time_delta(const char *prefix) -{ - unsigned long delta; - - gettimeofday(&end, NULL); - - delta = (end.tv_sec * 1000000 + end.tv_usec) - - (start.tv_sec * 1000000 + start.tv_usec); - - delta = delta; - - dbg("%s: %ld\n", prefix, delta); -} - static int subscribe_for_events(int fd) { int size_event = sizeof(event_type) / sizeof(event_type[0]); @@ -108,23 +88,14 @@ static int handle_v4l_events(struct video *vid) } switch (event.type) { - case V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT: - dbg("Port Reconfig recieved insufficient\n"); - break; - case V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT: - dbg("Setting changed sufficient\n"); + case V4L2_EVENT_EOS: + dbg("event eos"); break; - case V4L2_EVENT_MSM_VIDC_FLUSH_DONE: - dbg("Flush Done Recieved \n"); - break; - case V4L2_EVENT_MSM_VIDC_CLOSE_DONE: - dbg("Close Done Recieved \n"); - break; - case V4L2_EVENT_MSM_VIDC_SYS_ERROR: - dbg("SYS Error Recieved \n"); + case V4L2_EVENT_SOURCE_CHANGE: + dbg("event source change"); break; default: - dbg("unknown event type occurred %x\n", event.type); + dbg("unknown event type occurred %x", event.type); break; } @@ -137,106 +108,90 @@ void cleanup(struct instance *i) video_close(i); if (i->in.fd) input_close(i); + if (i->out.fd) + close(i->out.fd); } -int extract_and_process_header(struct instance *i) -{ - int used, fs; - int ret; - int n; - struct video *vid = &i->video; -#if 0 - ret = i->parser.func(&i->parser.ctx, - i->in.p + i->in.offs, - i->in.size - i->in.offs, - i->video.out_buf_addr[0], - i->video.out_buf_size, - &used, &fs, 1); - - if (ret == 0) { - err("Failed to extract header from stream"); - return -1; - } - - /* For H263 the header is passed with the first frame, so we should - * pass it again */ - if (i->parser.codec != V4L2_PIX_FMT_H263) - i->in.offs += used; - else - /* To do this we shall reset the stream parser to the initial - * configuration */ - parse_stream_init(&i->parser.ctx); - - dbg("Extracted header of size %d", fs); -#endif - ret = video_queue_buf_out(i, 0, fs); - if (ret) - return -1; - - dbg("queued output buffer %d", 0); - - i->video.out_buf_flag[0] = 1; -#if 1 - for (n = 1; n < vid->out_buf_cnt; n++) { - ret = video_queue_buf_out(i, n, 1); - if (ret) - return -1; - - i->video.out_buf_flag[n] = 1; - } -#endif - ret = video_stream(i, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - VIDIOC_STREAMON); - if (ret) - return -1; - - return 0; -} - -int save_frame(struct instance *i, const void *buf, unsigned int size) +int save_encoded(struct instance *i, const void *buf, unsigned int size) { mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; char filename[64]; - int fd; int ret; - static unsigned int frame_num = 0; + struct output *out = &i->out; + ssize_t written; - if (!i->save_frames) + if (!i->save_encoded) return 0; if (!i->save_path) - ret = sprintf(filename, "/mnt/frame%04d.nv12", frame_num); + ret = sprintf(filename, "/mnt/sdcard/encoded.h264"); else - ret = sprintf(filename, "%s/frame%04d.nv12", i->save_path, - frame_num); + ret = sprintf(filename, "%s/encoded.h264", i->save_path); if (ret < 0) { err("sprintf fail (%s)", strerror(errno)); return -1; } + if (out->fd) + goto write; + dbg("create file %s", filename); - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, mode); - if (fd < 0) { + out->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, mode); + if (out->fd < 0) { err("cannot open file (%s)", strerror(errno)); return -1; } - ret = write(fd, buf, size); - if (ret < 0) { +write: + written = pwrite(out->fd, buf, size, out->offs); + if (written < 0) { err("cannot write to file (%s)", strerror(errno)); return -1; } - close(fd); + out->offs += written; - frame_num++; + dbg("written %zd bytes at offset: %zu", written, out->offs); return 0; } -static int input_read(struct instance *i, unsigned int *used, unsigned int *fs) +static int input_read(struct instance *inst, unsigned int index, + unsigned int *used, unsigned int *fs) { + struct video *vid = &inst->video; + uint8_t *to = vid->out_buf_addr[index]; + int ret; + char filename[64]; + static int num = 1; + + *used = vid->out_buf_size; + *fs = vid->out_buf_size; + + ret = sprintf(filename, "/mnt/sdcard/frame%04d.nv12", num++); + if (ret < 0) { + err("sprintf fail (%s)", strerror(errno)); + return -1; + } + + dbg("open %s", filename); + + ret = input_open(inst, filename); + if (ret) + return ret; + + dbg("input nv12 size:%d, sizeimage:%d", inst->in.size, *fs); + + memcpy(to, inst->in.p, inst->in.size); + + input_close(inst); + + usleep(33 * 1000); + + if (num > 14) + num = 1; + return 0; } @@ -249,8 +204,6 @@ void *input_thread_func(void *args) unsigned int used, fs, n; int ret; - dbg("input thread started"); - while (!i->error && !i->finish) { n = 0; pthread_mutex_lock(&i->lock); @@ -260,28 +213,27 @@ void *input_thread_func(void *args) if (n < vid->out_buf_cnt) { - ret = input_read(i, &used, &fs); + ret = input_read(i, n, &used, &fs); + if (ret) + continue; - if (ret == 0 && i->in.offs == i->in.size) { - info("read all frames"); + if (vid->total_encoded >= i->num_frames_to_save) { i->finish = 1; fs = 0; } ret = video_queue_buf_out(i, n, fs); + if (ret) + continue; pthread_mutex_lock(&i->lock); vid->out_buf_flag[n] = 1; pthread_mutex_unlock(&i->lock); dbg("queued output buffer %d", n); - - i->in.offs += used; } } - dbg("input thread finished"); - return NULL; } @@ -293,8 +245,6 @@ void *main_thread_func(void *args) short revents; int ret, n, finished; - dbg("main thread started"); - pfd.fd = vid->fd; pfd.events = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLRDBAND | POLLPRI; @@ -327,21 +277,26 @@ void *main_thread_func(void *args) if (ret < 0) goto next_event; + pthread_mutex_lock(&i->lock); vid->cap_buf_flag[n] = 0; + pthread_mutex_unlock(&i->lock); - dbg("encoded frame %ld", vid->total_captured); + info("encoded frame %ld", vid->total_encoded); if (finished) break; - vid->total_captured++; + vid->total_encoded++; - save_frame(i, (void *)vid->cap_buf_addr[n][0], - bytesused); + save_encoded(i, (void *)vid->cap_buf_addr[n][0], + bytesused); ret = video_queue_buf_cap(i, n); - if (!ret) + if (!ret) { + pthread_mutex_lock(&i->lock); vid->cap_buf_flag[n] = 1; + pthread_mutex_unlock(&i->lock); + } } next_event: @@ -367,8 +322,6 @@ next_event: } } - dbg("main thread finished"); - return NULL; } @@ -376,8 +329,7 @@ int main(int argc, char **argv) { struct instance inst; struct video *vid = &inst.video; - pthread_t input_thread; - pthread_t main_thread; + pthread_t input_thread, main_thread; int ret, n; ret = parse_args(&inst, argc, argv); @@ -390,11 +342,11 @@ int main(int argc, char **argv) pthread_mutex_init(&inst.lock, 0); - vid->total_captured = 0; + vid->total_encoded = 0; - ret = input_open(&inst, inst.in.name); - if (ret) - goto err; +// ret = input_open(&inst, inst.in.name); +// if (ret) +// goto err; ret = video_open(&inst, inst.video.name); if (ret) @@ -405,13 +357,6 @@ int main(int argc, char **argv) if (ret) goto err; #endif - ret = video_setup_output(&inst, inst.codec, STREAM_BUUFER_SIZE, 1); - if (ret) - goto err; - -// ret = video_set_control(&inst); -// if (ret) -// goto err; if (inst.use_dmabuf) ret = video_setup_capture_dmabuf(&inst, 2, inst.width, @@ -421,14 +366,13 @@ int main(int argc, char **argv) if (ret) goto err; - ret = extract_and_process_header(&inst); + ret = video_setup_output(&inst, inst.codec, STREAM_BUUFER_SIZE, 1); if (ret) goto err; -#if 0 - for (n = 0; n < vid->cap_buf_cnt; n++) - video_export_buf(&inst, n); -#endif + ret = video_set_control(&inst); + if (ret) + goto err; /* queue all capture buffers */ for (n = 0; n < vid->cap_buf_cnt; n++) { @@ -444,6 +388,22 @@ int main(int argc, char **argv) if (ret) goto err; + ret = video_stream(&inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + VIDIOC_STREAMON); + if (ret) + goto err; + + if (0) { + struct v4l2_control cntrl; + + cntrl.id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_SEQ_HEADER; + cntrl.value = 1; + + ret = ioctl(inst.video.fd, VIDIOC_S_CTRL, &cntrl); + if (ret) + err("request seq header failed (%s)", strerror(errno)); + } + dbg("Launching threads"); if (pthread_create(&input_thread, NULL, input_thread_func, &inst)) @@ -459,7 +419,7 @@ int main(int argc, char **argv) video_stop(&inst); - info("Total frames encoded %ld", vid->total_captured); + info("Total frames encoded %ld", vid->total_encoded); cleanup(&inst); @@ -28,6 +28,7 @@ #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> +#include <sys/time.h> #include <unistd.h> #include <errno.h> @@ -43,7 +44,7 @@ int video_open(struct instance *i, char *name) i->video.fd = open(name, O_RDWR, 0); if (i->video.fd < 0) { - err("Failed to open video decoder: %s", name); + err("Failed to open video encoder: %s", name); return -1; } @@ -75,15 +76,44 @@ void video_close(struct instance *i) int video_set_control(struct instance *i) { - struct v4l2_control control = {0}; + struct v4l2_streamparm parm; + struct v4l2_control cntrl; int ret; - control.id = V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER; - control.value = 1; + parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + parm.parm.output.timeperframe.numerator = 1; + parm.parm.output.timeperframe.denominator = 30; - ret = ioctl(i->video.fd, VIDIOC_S_CTRL, &control); + info("setting framerate"); + ret = ioctl(i->video.fd, VIDIOC_S_PARM, &parm); + if (ret) + err("set framerate (%s)", strerror(errno)); - return ret; + cntrl.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + cntrl.value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + + info("setting profile"); + ret = ioctl(i->video.fd, VIDIOC_S_CTRL, &cntrl); + if (ret) + err("set control - profile (%s)", strerror(errno)); + + cntrl.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL; + cntrl.value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + + info("setting level"); + ret = ioctl(i->video.fd, VIDIOC_S_CTRL, &cntrl); + if (ret) + err("set control - level (%s)", strerror(errno)); + + cntrl.id = V4L2_CID_MPEG_VIDEO_BITRATE; + cntrl.value = 64000; + + info("setting bitrate"); + ret = ioctl(i->video.fd, VIDIOC_S_CTRL, &cntrl); + if (ret) + err("set control - bitrate (%s)", strerror(errno)); + + return 0; } int video_export_buf(struct instance *i, int index) @@ -120,6 +150,7 @@ static int video_queue_buf(struct instance *i, int index, int l1, int l2, struct video *vid = &i->video; struct v4l2_buffer buf; struct v4l2_plane planes[2]; + struct timeval tv; int ret; memzero(buf); @@ -142,6 +173,12 @@ static int video_queue_buf(struct instance *i, int index, int l1, int l2, buf.m.planes[0].length = vid->out_buf_size; if (l1 == 0) buf.flags |= V4L2_QCOM_BUF_FLAG_EOS; + + ret = gettimeofday(&tv, NULL); + if (ret) + err("getting timeofday (%s)", strerror(errno)); + + buf.timestamp = tv; } ret = ioctl(vid->fd, VIDIOC_QBUF, &buf); @@ -302,8 +339,6 @@ int video_stream(struct instance *i, enum v4l2_buf_type type, int status) int video_stop(struct instance *i) { - struct video *vid = &i->video; - struct v4l2_decoder_cmd dec; int ret; ret = video_stream(i, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, @@ -332,10 +367,10 @@ int video_setup_capture(struct instance *i, int count, int w, int h) fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; fmt.fmt.pix_mp.height = h; fmt.fmt.pix_mp.width = w; - fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; + fmt.fmt.pix_mp.pixelformat = i->codec; ret = ioctl(vid->fd, VIDIOC_S_FMT, &fmt); if (ret) { - err("Failed to set format (%dx%d)", w, h); + err("Failed to set format (%dx%d) (%s)", w, h, strerror(errno)); return -1; } @@ -349,7 +384,7 @@ int video_setup_capture(struct instance *i, int count, int w, int h) vid->cap_buf_cnt_min = 1; vid->cap_buf_queued = 0; - dbg("video decoder buffer parameters: %dx%d plane[0]=%d plane[1]=%d", + info("video CAPTURE buffer parameters: %dx%d plane[0]=%d plane[1]=%d", fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, vid->cap_buf_size[0], vid->cap_buf_size[1]); @@ -364,11 +399,11 @@ int video_setup_capture(struct instance *i, int count, int w, int h) return -1; } - info("Number of CAPTURE buffers is %d (requested %d, extra %d)", - reqbuf.count, vid->cap_buf_cnt, 0); - vid->cap_buf_cnt = reqbuf.count; + info("Number of CAPTURE buffers is %d (requested %d)", + vid->cap_buf_cnt, count); + for (n = 0; n < vid->cap_buf_cnt; n++) { memzero(buf); memset(planes, 0, sizeof(planes)); @@ -433,7 +468,7 @@ int video_setup_capture_dmabuf(struct instance *i, int count, int w, int h) vid->cap_buf_cnt_min = 1; vid->cap_buf_queued = 0; - dbg("video decoder buffer parameters: %dx%d plane[0]=%d plane[1]=%d", + dbg("video CAPTURE buffer parameters: %dx%d plane[0]=%d plane[1]=%d", fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, vid->cap_buf_size[0], vid->cap_buf_size[1]); @@ -473,7 +508,7 @@ int video_setup_output(struct instance *i, unsigned long codec, try_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; try_fmt.fmt.pix_mp.width = i->width; try_fmt.fmt.pix_mp.height = i->height; - try_fmt.fmt.pix_mp.pixelformat = codec; + try_fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; ret = ioctl(vid->fd, VIDIOC_TRY_FMT, &try_fmt); if (ret) { @@ -487,7 +522,7 @@ int video_setup_output(struct instance *i, unsigned long codec, fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; fmt.fmt.pix_mp.width = i->width; fmt.fmt.pix_mp.height = i->height; - fmt.fmt.pix_mp.pixelformat = codec; + fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; ret = ioctl(vid->fd, VIDIOC_S_FMT, &fmt); if (ret) { @@ -495,8 +530,8 @@ int video_setup_output(struct instance *i, unsigned long codec, return -1; } - dbg("Setup decoding OUTPUT buffer size=%u (requested=%u)", - fmt.fmt.pix_mp.plane_fmt[0].sizeimage, size); + info("Setup OUTPUT buffer size=%u (requested=%u)", + fmt.fmt.pix_mp.plane_fmt[0].sizeimage, size); vid->out_buf_size = fmt.fmt.pix_mp.plane_fmt[0].sizeimage; @@ -508,7 +543,7 @@ int video_setup_output(struct instance *i, unsigned long codec, if (ret) { err("Failed to get format on OUTPUT (%s)", strerror(errno)); } else { - err("Get format sizeimage is %d", + info("Get format sizeimage is %d", g_fmt.fmt.pix_mp.plane_fmt[0].sizeimage); } @@ -525,8 +560,8 @@ int video_setup_output(struct instance *i, unsigned long codec, vid->out_buf_cnt = reqbuf.count; - dbg("Number of video decoder OUTPUT buffers is %d (requested %d)", - vid->out_buf_cnt, count); + info("Number of OUTPUT buffers is %d (requested %d)", + vid->out_buf_cnt, count); for (n = 0; n < vid->out_buf_cnt; n++) { memzero(buf); |