summaryrefslogtreecommitdiff
path: root/stan/v4l2-decode/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'stan/v4l2-decode/main.c')
-rw-r--r--stan/v4l2-decode/main.c487
1 files changed, 487 insertions, 0 deletions
diff --git a/stan/v4l2-decode/main.c b/stan/v4l2-decode/main.c
new file mode 100644
index 0000000..ddb5b8d
--- /dev/null
+++ b/stan/v4l2-decode/main.c
@@ -0,0 +1,487 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Main file of the application
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2015 Linaro Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <poll.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "args.h"
+#include "common.h"
+#include "fileops.h"
+#include "video.h"
+#include "parser.h"
+#include "tracer.h"
+#include "ctrls.h"
+
+#define STREAM_BUFFER_SIZE (4 * 1024 * 1024)
+
+static int event_recv = 0;
+
+static const unsigned int event_types[] = {
+ V4L2_EVENT_EOS,
+ V4L2_EVENT_SOURCE_CHANGE,
+};
+
+int events_unsubscribe(struct video *vid)
+{
+ int size_event = sizeof(event_types) / sizeof(event_types[0]);
+ int i, ret;
+
+ for (i = 0; i < size_event; i++) {
+ ret = video_event_unsubscribe(vid, event_types[i]);
+ if (ret < 0) {
+ err("cannot unsubscribe event type %d (%s)",
+ event_types[i], strerror(errno));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int events_subscribe(struct video *vid)
+{
+ int n_events = sizeof(event_types) / sizeof(event_types[0]);
+ int i, ret;
+
+ for (i = 0; i < n_events; i++) {
+ ret = video_event_subscribe(vid, event_types[i]);
+ if (ret < 0) {
+ err("cannot subscribe event type %d (%s)",
+ event_types[i], strerror(errno));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int events_handler(struct instance *inst)
+{
+ struct video *vid = &inst->video;
+ struct v4l2_event event;
+ unsigned int w, h;
+ int ret;
+
+ memset(&event, 0, sizeof(event));
+
+ ret = video_event_dequeue(vid, &event);
+ if (ret < 0) {
+ err("cannot dequeue events (%s)", strerror(errno));
+ return -errno;
+ }
+
+ switch (event.type) {
+ case V4L2_EVENT_EOS:
+ info("EOS reached");
+ break;
+ case V4L2_EVENT_SOURCE_CHANGE:
+ info("Source changed");
+ ret = video_gfmt_cap(vid, &w, &h, NULL);
+ if (!ret)
+ info("new resolution is %ux%u", w, h);
+
+ ctrl_get_min_bufs_for_capture(inst);
+ event_recv = 1;
+ break;
+ default:
+ dbg("unknown event type occurred %x", event.type);
+ break;
+ }
+
+ return 0;
+}
+
+static int save_frame(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;
+
+ if (!i->save_frames)
+ return 0;
+
+ ret = sprintf(filename, "%s/frame%04d.nv12", i->save_path, frame_num);
+ if (ret < 0) {
+ err("sprintf fail (%s)", strerror(errno));
+ return -1;
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, mode);
+ if (fd < 0) {
+ err("cannot open file (%s)", strerror(errno));
+ return -1;
+ }
+
+ ret = write(fd, buf, size);
+ if (ret < 0) {
+ err("cannot write to file (%s)", strerror(errno));
+ return -1;
+ }
+
+ close(fd);
+
+ frame_num++;
+
+ return 0;
+}
+
+static void *parser_thread_func(void *args)
+{
+ struct instance *inst = args;
+ struct video *vid = &inst->video;
+ struct parser_context *parser = inst->parser_ctx;
+ unsigned int idx, fs;
+ int ret, last_parser_pkt = 0;
+
+ dbg("Parser thread started");
+
+ while (!inst->error && !inst->finish) {
+ idx = 0;
+ pthread_mutex_lock(&inst->lock);
+ while (idx < vid->out_buf_cnt && vid->out_buf_flag[idx])
+ idx++;
+ pthread_mutex_unlock(&inst->lock);
+
+ if (idx < vid->out_buf_cnt) {
+
+ if (last_parser_pkt) {
+ inst->finish = 1;
+// fs = 0;
+// ret = video_qbuf_out(vid, idx, fs);
+// pthread_mutex_lock(&inst->lock);
+// vid->out_buf_flag[idx] = 1;
+// pthread_mutex_unlock(&inst->lock);
+ continue;
+ }
+
+ ret = parser_read_frame(parser,
+ vid->out_buf_addr[idx],
+ vid->out_buf_size,
+ &fs,
+ 0);
+
+ if (!ret || (parser->input.offs == parser->input.size)) {
+ dbg("Last parser frame (fs: %u)", fs);
+ last_parser_pkt = 1;
+ }
+
+ dbg("Extracted frame of size %d", fs);
+
+ if (fs > vid->out_buf_size)
+ err("consumed %u, out buf sz %u",
+ fs, vid->out_buf_size);
+
+ ret = video_qbuf_out(vid, idx, fs);
+
+ pthread_mutex_lock(&inst->lock);
+ vid->out_buf_flag[idx] = 1;
+ pthread_mutex_unlock(&inst->lock);
+ } else {
+ pthread_mutex_lock(&inst->lock);
+ pthread_cond_wait(&inst->cond, &inst->lock);
+ pthread_mutex_unlock(&inst->lock);
+ }
+ }
+
+ dbg("Parser thread finished");
+
+ return NULL;
+}
+
+static int handle_capture(struct instance *inst)
+{
+ struct video *vid = &inst->video;
+ unsigned int bytesused, idx, finished, seq = 0;
+ int ret;
+
+ dbg("dequeuing capture buffer");
+
+ tracer_buf_start(inst, TYPE_DQBUF);
+
+ ret = video_dqbuf_cap(vid, &idx, &finished, &bytesused, &seq);
+
+ tracer_buf_finish(inst, TYPE_DQBUF);
+
+ if (ret < 0)
+ goto done;
+
+ vid->cap_buf_flag[idx] = 0;
+
+ fprintf(stdout, "%08ld\b\b\b\b\b\b\b\b", vid->total_captured);
+ fflush(stdout);
+
+ if (finished)
+ goto done;
+
+ vid->total_captured++;
+
+ save_frame(inst, vid->cap_buf_addr[idx][0], bytesused);
+
+ tracer_buf_start(inst, TYPE_QBUF);
+
+ ret = video_qbuf_cap(vid, idx);
+
+ tracer_buf_finish(inst, TYPE_QBUF);
+
+ if (ret < 0)
+ err("qbuf capture error %d", ret);
+
+done:
+ if (!ret)
+ vid->cap_buf_flag[idx] = 1;
+
+ dbg("seq: %u, captured: %lu\n", seq, vid->total_captured);
+
+ return ret;
+}
+
+static int handle_output(struct instance *inst)
+{
+ struct video *vid = &inst->video;
+ unsigned int idx;
+ int ret;
+
+ ret = video_dqbuf_out(vid, &idx);
+ if (ret < 0) {
+ err("dequeue output buffer fail");
+ } else {
+ pthread_mutex_lock(&inst->lock);
+ vid->out_buf_flag[idx] = 0;
+ pthread_mutex_unlock(&inst->lock);
+ pthread_cond_signal(&inst->cond);
+ }
+
+ return ret;
+}
+
+static void *main_thread_func(void *args)
+{
+ struct instance *inst = args;
+ struct video *vid = &inst->video;
+ struct pollfd pfd;
+ short revents;
+ int ret;
+
+ dbg("main thread started");
+
+ pfd.fd = vid->fd;
+ pfd.events = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM |
+ POLLRDBAND | POLLPRI;
+
+ fprintf(stdout, "decoded frame ");
+
+ while (1) {
+ if (inst->finish)
+ break;
+
+ ret = poll(&pfd, 1, 2000);
+ if (!ret) {
+ inst->error = 1;
+ err("poll timeout");
+ break;
+ } else if (ret < 0) {
+ inst->error = 1;
+ err("poll error");
+ break;
+ }
+
+ revents = pfd.revents;
+
+ if (revents & POLLPRI)
+ events_handler(inst);
+
+ if (revents & (POLLIN | POLLRDNORM)) {
+ ret = handle_capture(inst);
+ if (ret)
+ break;
+ }
+
+ if (revents & (POLLOUT | POLLWRNORM)) {
+ ret = handle_output(inst);
+ if (ret)
+ break;
+ }
+ }
+
+ if (inst->error)
+ pthread_cond_signal(&inst->cond);
+
+ dbg("main thread finished");
+
+ return NULL;
+}
+
+static void cleanup(struct instance *inst)
+{
+ video_close(&inst->video);
+ parser_destroy(inst->parser_ctx);
+}
+
+static int drain_loop(struct instance *inst)
+{
+ int ret;
+
+ ret = video_dec_stop(&inst->video);
+ if (ret)
+ err("decoder stop command fail %d", ret);
+
+ while (1) {
+ ret = handle_capture(inst);
+ if (ret == -EPIPE || ret == -EIO)
+ break;
+ }
+
+ /* Drain loop should exits with EPIPE */
+ if (ret != -EPIPE)
+ err("Drain loop exits with error %d", ret);
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ struct instance inst;
+ struct video *vid = &inst.video;
+ pthread_t parser_thread;
+ pthread_t main_thread;
+ int ret, n;
+
+ memset(&inst, 0, sizeof(inst));
+
+ ret = parse_args(&inst, argc, argv);
+ if (ret) {
+ print_usage(argv[0]);
+ return -1;
+ }
+
+ info("decoding resolution %dx%d", inst.width, inst.height);
+
+ pthread_mutex_init(&inst.lock, 0);
+ pthread_condattr_init(&inst.attr);
+ pthread_cond_init(&inst.cond, &inst.attr);
+
+ ret = tracer_init(&inst);
+ if (ret)
+ goto err;
+
+ ret = parser_create(&inst.parser_ctx, inst.file_name, inst.codec, 0, NULL);
+ if (ret)
+ goto err;
+
+ ret = video_open(vid, inst.video.name);
+ if (ret)
+ goto err;
+
+ ret = events_subscribe(vid);
+ if (ret)
+ goto err;
+
+ ret = video_set_framerate(vid, 30);
+ if (ret)
+ goto err;
+
+ ret = video_sfmt_out(vid, inst.width, inst.height, inst.codec);
+ if (ret)
+ goto err;
+
+ ret = video_setup_output(vid, STREAM_BUFFER_SIZE, 4);
+ if (ret)
+ goto err;
+
+ ret = video_set_control(vid);
+ if (ret)
+ goto err;
+
+ ret = video_streamon_out(vid);
+ if (ret)
+ goto err;
+
+ if (pthread_create(&parser_thread, NULL, parser_thread_func, &inst))
+ goto err;
+
+ if (pthread_create(&main_thread, NULL, main_thread_func, &inst))
+ goto err;
+
+ while (!event_recv)
+ usleep(1000);
+ event_recv = 0;
+
+ info("event received");
+
+ ret = video_setup_capture(vid, 4, inst.width, inst.height);
+ if (ret)
+ goto err;
+
+ ret = video_streamon_cap(vid);
+ if (ret)
+ goto err;
+
+ for (n = 0; n < vid->cap_buf_cnt; n++) {
+ if (inst.video.cap_use_dmabuf)
+ ret = video_qbuf_cap_dmabuf(vid, n, 0);
+ else
+ ret = video_qbuf_cap(vid, n);
+
+ if (ret)
+ goto err;
+
+ vid->cap_buf_flag[n] = 1;
+ }
+
+ pthread_join(parser_thread, 0);
+ pthread_join(main_thread, 0);
+
+ dbg("Threads have finished");
+
+ if (!inst.error && inst.finish)
+ ret = drain_loop(&inst);
+
+ video_stop(vid);
+ events_unsubscribe(vid);
+ tracer_show(&inst);
+ tracer_deinit(&inst);
+ pthread_mutex_destroy(&inst.lock);
+ pthread_cond_destroy(&inst.cond);
+ pthread_condattr_destroy(&inst.attr);
+
+ cleanup(&inst);
+
+ info("Total frames captured %ld", vid->total_captured);
+
+ return 0;
+err:
+ cleanup(&inst);
+ return 1;
+}