mirror of
git://nv-tegra.nvidia.com/tegra/argus-cam-libav/argus_cam_libavencoder.git
synced 2025-12-22 09:12:30 +03:00
Updating prebuilts and/or headers
7d8cfb5596b00c49ca01c4d59ee74c8b88059909 - 19_argus_camera_sw_encode/Makefile 0237b5299525c072e55e721dcedbfd3d06e182ca - 19_argus_camera_sw_encode/FFMPEG-LICENSE.md de91caa771262821be9e97cfe54154efd19b3741 - 19_argus_camera_sw_encode/argus_camera_sw_encode_main.cpp 7b299759a546d9c7ed36d7ab0db92bcd15eb5892 - 19_argus_camera_sw_encode/argus_camera_sw_encode.h Change-Id: Id16016f5bb68baba070a0e4d956af4a0d9c62d2a
This commit is contained in:
111
19_argus_camera_sw_encode/FFMPEG-LICENSE.md
Normal file
111
19_argus_camera_sw_encode/FFMPEG-LICENSE.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# License
|
||||||
|
|
||||||
|
Most files in FFmpeg are under the GNU Lesser General Public License version 2.1
|
||||||
|
or later (LGPL v2.1+). Read the file `COPYING.LGPLv2.1` for details. Some other
|
||||||
|
files have MIT/X11/BSD-style licenses. In combination the LGPL v2.1+ applies to
|
||||||
|
FFmpeg.
|
||||||
|
|
||||||
|
Some optional parts of FFmpeg are licensed under the GNU General Public License
|
||||||
|
version 2 or later (GPL v2+). See the file `COPYING.GPLv2` for details. None of
|
||||||
|
these parts are used by default, you have to explicitly pass `--enable-gpl` to
|
||||||
|
configure to activate them. In this case, FFmpeg's license changes to GPL v2+.
|
||||||
|
|
||||||
|
Specifically, the GPL parts of FFmpeg are:
|
||||||
|
|
||||||
|
- libpostproc
|
||||||
|
- optional x86 optimization in the files
|
||||||
|
- `libavcodec/x86/flac_dsp_gpl.asm`
|
||||||
|
- `libavcodec/x86/idct_mmx.c`
|
||||||
|
- `libavfilter/x86/vf_removegrain.asm`
|
||||||
|
- the following building and testing tools
|
||||||
|
- `compat/solaris/make_sunver.pl`
|
||||||
|
- `doc/t2h.pm`
|
||||||
|
- `doc/texi2pod.pl`
|
||||||
|
- `libswresample/swresample-test.c`
|
||||||
|
- `tests/checkasm/*`
|
||||||
|
- `tests/tiny_ssim.c`
|
||||||
|
- the following filters in libavfilter:
|
||||||
|
- `vf_blackframe.c`
|
||||||
|
- `vf_boxblur.c`
|
||||||
|
- `vf_colormatrix.c`
|
||||||
|
- `vf_cover_rect.c`
|
||||||
|
- `vf_cropdetect.c`
|
||||||
|
- `vf_delogo.c`
|
||||||
|
- `vf_eq.c`
|
||||||
|
- `vf_find_rect.c`
|
||||||
|
- `vf_fspp.c`
|
||||||
|
- `vf_geq.c`
|
||||||
|
- `vf_histeq.c`
|
||||||
|
- `vf_hqdn3d.c`
|
||||||
|
- `vf_interlace.c`
|
||||||
|
- `vf_kerndeint.c`
|
||||||
|
- `vf_mcdeint.c`
|
||||||
|
- `vf_mpdecimate.c`
|
||||||
|
- `vf_owdenoise.c`
|
||||||
|
- `vf_perspective.c`
|
||||||
|
- `vf_phase.c`
|
||||||
|
- `vf_pp.c`
|
||||||
|
- `vf_pp7.c`
|
||||||
|
- `vf_pullup.c`
|
||||||
|
- `vf_repeatfields.c`
|
||||||
|
- `vf_sab.c`
|
||||||
|
- `vf_smartblur.c`
|
||||||
|
- `vf_spp.c`
|
||||||
|
- `vf_stereo3d.c`
|
||||||
|
- `vf_super2xsai.c`
|
||||||
|
- `vf_tinterlace.c`
|
||||||
|
- `vf_uspp.c`
|
||||||
|
- `vsrc_mptestsrc.c`
|
||||||
|
|
||||||
|
Should you, for whatever reason, prefer to use version 3 of the (L)GPL, then
|
||||||
|
the configure parameter `--enable-version3` will activate this licensing option
|
||||||
|
for you. Read the file `COPYING.LGPLv3` or, if you have enabled GPL parts,
|
||||||
|
`COPYING.GPLv3` to learn the exact legal terms that apply in this case.
|
||||||
|
|
||||||
|
There are a handful of files under other licensing terms, namely:
|
||||||
|
|
||||||
|
* The files `libavcodec/jfdctfst.c`, `libavcodec/jfdctint_template.c` and
|
||||||
|
`libavcodec/jrevdct.c` are taken from libjpeg, see the top of the files for
|
||||||
|
licensing details. Specifically note that you must credit the IJG in the
|
||||||
|
documentation accompanying your program if you only distribute executables.
|
||||||
|
You must also indicate any changes including additions and deletions to
|
||||||
|
those three files in the documentation.
|
||||||
|
* `tests/reference.pnm` is under the expat license.
|
||||||
|
|
||||||
|
|
||||||
|
## External libraries
|
||||||
|
|
||||||
|
FFmpeg can be combined with a number of external libraries, which sometimes
|
||||||
|
affect the licensing of binaries resulting from the combination.
|
||||||
|
|
||||||
|
### Compatible libraries
|
||||||
|
|
||||||
|
The following libraries are under GPL:
|
||||||
|
- frei0r
|
||||||
|
- libcdio
|
||||||
|
- librubberband
|
||||||
|
- libvidstab
|
||||||
|
- libx264
|
||||||
|
- libx265
|
||||||
|
- libxavs
|
||||||
|
- libxvid
|
||||||
|
|
||||||
|
When combining them with FFmpeg, FFmpeg needs to be licensed as GPL as well by
|
||||||
|
passing `--enable-gpl` to configure.
|
||||||
|
|
||||||
|
The OpenCORE and VisualOn libraries are under the Apache License 2.0. That
|
||||||
|
license is incompatible with the LGPL v2.1 and the GPL v2, but not with
|
||||||
|
version 3 of those licenses. So to combine these libraries with FFmpeg, the
|
||||||
|
license version needs to be upgraded by passing `--enable-version3` to configure.
|
||||||
|
|
||||||
|
### Incompatible libraries
|
||||||
|
|
||||||
|
There are certain libraries you can combine with FFmpeg whose licenses are not
|
||||||
|
compatible with the GPL and/or the LGPL. If you wish to enable these
|
||||||
|
libraries, even in circumstances that their license may be incompatible, pass
|
||||||
|
`--enable-nonfree` to configure. This will cause the resulting binary to be
|
||||||
|
unredistributable.
|
||||||
|
|
||||||
|
The Fraunhofer FDK AAC and OpenSSL libraries are under licenses which are
|
||||||
|
incompatible with the GPLv2 and v3. To the best of our knowledge, they are
|
||||||
|
compatible with the LGPL.
|
||||||
51
19_argus_camera_sw_encode/Makefile
Normal file
51
19_argus_camera_sw_encode/Makefile
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# 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 ../Rules.mk
|
||||||
|
|
||||||
|
APP := camera_sw_encode
|
||||||
|
|
||||||
|
ARGUS_UTILS_DIR := $(TOP_DIR)/argus/samples/utils
|
||||||
|
|
||||||
|
SRCS := \
|
||||||
|
argus_camera_sw_encode_main.cpp \
|
||||||
|
$(wildcard $(CLASS_DIR)/*.cpp) \
|
||||||
|
$(ARGUS_UTILS_DIR)/Thread.cpp \
|
||||||
|
$(ARGUS_UTILS_DIR)/NativeBuffer.cpp \
|
||||||
|
$(ARGUS_UTILS_DIR)/nvmmapi/NvNativeBuffer.cpp
|
||||||
|
|
||||||
|
OBJS := $(SRCS:.cpp=.o)
|
||||||
|
|
||||||
|
CPPFLAGS += \
|
||||||
|
-I"$(ARGUS_UTILS_DIR)"
|
||||||
|
|
||||||
|
LDFLAGS += \
|
||||||
|
-lnvargus_socketclient -lavcodec -lavutil
|
||||||
|
|
||||||
|
all: $(APP)
|
||||||
|
|
||||||
|
$(CLASS_DIR)/%.o: $(CLASS_DIR)/%.cpp
|
||||||
|
$(AT)$(MAKE) -C $(CLASS_DIR)
|
||||||
|
|
||||||
|
%.o: %.cpp
|
||||||
|
@echo "Compiling: $<"
|
||||||
|
$(CPP) $(CPPFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(APP): $(OBJS)
|
||||||
|
@echo "Linking: $@"
|
||||||
|
$(CPP) -o $@ $(OBJS) $(CPPFLAGS) $(LDFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(AT)rm -rf $(APP) $(OBJS)
|
||||||
162
19_argus_camera_sw_encode/argus_camera_sw_encode.h
Normal file
162
19_argus_camera_sw_encode/argus_camera_sw_encode.h
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ARGUS_CAMERA_SW_ENCODE_H__
|
||||||
|
#define __ARGUS_CAMERA_SW_ENCODE_H__
|
||||||
|
|
||||||
|
#include "Error.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
#include "nvmmapi/NvNativeBuffer.h"
|
||||||
|
#include "NvBuffer.h"
|
||||||
|
#include "NvBufSurface.h"
|
||||||
|
|
||||||
|
#include <Argus/Argus.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavutil/opt.h>
|
||||||
|
#include <libavutil/imgutils.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace Argus;
|
||||||
|
|
||||||
|
/* Specifies the input pixel format */
|
||||||
|
enum pix_fmt
|
||||||
|
{
|
||||||
|
NV12 = 1,
|
||||||
|
I420
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Debug print macros */
|
||||||
|
#define PRODUCER_PRINT(...) printf("PRODUCER: " __VA_ARGS__)
|
||||||
|
#define CONSUMER_PRINT(...) printf("CONSUMER: " __VA_ARGS__)
|
||||||
|
#define CHECK_ERROR(expr) \
|
||||||
|
do { \
|
||||||
|
if ((expr) < 0) { \
|
||||||
|
abort(); \
|
||||||
|
ORIGINATE_ERROR(#expr " failed"); \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
namespace ArgusSamples
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to map NvNativeBuffer to Argus::Buffer and vice versa.
|
||||||
|
* A reference to DmaBuffer will be saved as client data in each Argus::Buffer.
|
||||||
|
* Also DmaBuffer will keep a reference to corresponding Argus::Buffer.
|
||||||
|
* This class also extends NvBuffer to act as a share buffer between Argus and encoder.
|
||||||
|
*/
|
||||||
|
class DmaBuffer : public NvNativeBuffer, public NvBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/* Always use this static method to create DmaBuffer */
|
||||||
|
static DmaBuffer* create(const Argus::Size2D<uint32_t>& size,
|
||||||
|
NvBufSurfaceColorFormat colorFormat,
|
||||||
|
NvBufSurfaceLayout layout = NVBUF_LAYOUT_PITCH);
|
||||||
|
|
||||||
|
/* Help function to convert Argus Buffer to DmaBuffer */
|
||||||
|
static DmaBuffer* fromArgusBuffer(Buffer *buffer);
|
||||||
|
|
||||||
|
/* Return DMA buffer handle */
|
||||||
|
int getFd() const { return m_fd; }
|
||||||
|
|
||||||
|
/* Get and set reference to Argus buffer */
|
||||||
|
void setArgusBuffer(Buffer *buffer) { m_buffer = buffer; }
|
||||||
|
Buffer *getArgusBuffer() const { return m_buffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
DmaBuffer(const Argus::Size2D<uint32_t>& size)
|
||||||
|
: NvNativeBuffer(size),
|
||||||
|
NvBuffer(0, 0),
|
||||||
|
m_buffer(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer *m_buffer; /**< Holds the reference to Argus::Buffer */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumer thread:
|
||||||
|
* Acquire frames from BufferOutputStream and extract the DMABUF fd from it.
|
||||||
|
* Provide DMABUF to V4L2 for video encoding. The encoder will save the encoded
|
||||||
|
* stream to disk.
|
||||||
|
*/
|
||||||
|
class ConsumerThread : public Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ConsumerThread(OutputStream* stream);
|
||||||
|
~ConsumerThread();
|
||||||
|
|
||||||
|
bool isInError()
|
||||||
|
{
|
||||||
|
return m_gotError;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** @name Thread methods */
|
||||||
|
/**@{*/
|
||||||
|
virtual bool threadInitialize();
|
||||||
|
virtual bool threadExecute();
|
||||||
|
virtual bool threadShutdown();
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/* Initialize and set properties of Libav Encoder */
|
||||||
|
bool createVideoEncoder();
|
||||||
|
|
||||||
|
/* Destroy the Libav Encoder */
|
||||||
|
bool destroyVideoEncoder();
|
||||||
|
|
||||||
|
/* Perform Encode operation */
|
||||||
|
int libavEncode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, int fd);
|
||||||
|
|
||||||
|
/* Abort the encode operation */
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
OutputStream* m_stream; /**< Holds the Output stream */
|
||||||
|
const AVCodec* codec; /**< Holds the encoder type and name */
|
||||||
|
AVCodecContext* m_enc_ctx; /**< Holds the libav codec context */
|
||||||
|
AVFrame* m_frame; /**< Holds the libav input frame data and info */
|
||||||
|
AVPacket* m_pkt; /**< Holds the libav output packet data and info */
|
||||||
|
std::ofstream* m_outputFile;
|
||||||
|
bool m_gotError;
|
||||||
|
NvBufSurface* m_blitSurf; /**< Holds the I420 Buffer if applicable */
|
||||||
|
NvBufSurface** m_sysBuffers; /**< Holds the system buffers */
|
||||||
|
int m_count; /**< Holds the count of buffers sent */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Argus Producer thread:
|
||||||
|
* Opens the Argus camera driver, creates an BufferOutputStream to output
|
||||||
|
* frames, then performs repeating capture requests for CAPTURE_TIME
|
||||||
|
* seconds before closing the producer and Argus driver.
|
||||||
|
*/
|
||||||
|
bool execute();
|
||||||
|
|
||||||
|
} /* namespace ArgusSamples */
|
||||||
|
|
||||||
|
/* Function to parse commandline arguments */
|
||||||
|
bool parseCmdline(int argc, char **argv);
|
||||||
|
|
||||||
|
/* Function to print help */
|
||||||
|
void printHelp();
|
||||||
|
|
||||||
|
#endif // __ARGUS_CAMERA_SW_ENCODE_H__
|
||||||
668
19_argus_camera_sw_encode/argus_camera_sw_encode_main.cpp
Normal file
668
19_argus_camera_sw_encode/argus_camera_sw_encode_main.cpp
Normal file
@@ -0,0 +1,668 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "argus_camera_sw_encode.h"
|
||||||
|
|
||||||
|
using namespace ArgusSamples;
|
||||||
|
|
||||||
|
/* Constant configuration */
|
||||||
|
const int MAX_ENCODER_FRAMES = 5;
|
||||||
|
const int DEFAULT_FPS = 30;
|
||||||
|
enum pix_fmt ENCODER_INPIXFMT = NV12;
|
||||||
|
|
||||||
|
/* This value is tricky.
|
||||||
|
Too small value will impact the FPS */
|
||||||
|
const int NUM_BUFFERS = 10;
|
||||||
|
|
||||||
|
/* Configurations which can be overrided by cmdline */
|
||||||
|
int CAPTURE_TIME = 5; // In seconds.
|
||||||
|
uint32_t CAMERA_INDEX = 0;
|
||||||
|
Size2D<uint32_t> STREAM_SIZE (640, 480);
|
||||||
|
std::string OUTPUT_FILENAME ("output.h264");
|
||||||
|
bool VERBOSE_ENABLE = false;
|
||||||
|
|
||||||
|
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
|
||||||
|
|
||||||
|
DmaBuffer* DmaBuffer::create(const Argus::Size2D<uint32_t>& size,
|
||||||
|
NvBufSurfaceColorFormat colorFormat,
|
||||||
|
NvBufSurfaceLayout layout)
|
||||||
|
{
|
||||||
|
DmaBuffer* buffer = new DmaBuffer(size);
|
||||||
|
if (!buffer)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
NvBufSurf::NvCommonAllocateParams cParams;
|
||||||
|
|
||||||
|
cParams.memtag = NvBufSurfaceTag_CAMERA;
|
||||||
|
cParams.width = size.width();
|
||||||
|
cParams.height = size.height();
|
||||||
|
cParams.colorFormat = colorFormat;
|
||||||
|
cParams.layout = layout;
|
||||||
|
cParams.memType = NVBUF_MEM_SURFACE_ARRAY;
|
||||||
|
|
||||||
|
if (NvBufSurf::NvAllocate(&cParams, 1, &buffer->m_fd))
|
||||||
|
{
|
||||||
|
delete buffer;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
DmaBuffer* DmaBuffer::fromArgusBuffer(Buffer *buffer)
|
||||||
|
{
|
||||||
|
IBuffer* iBuffer = interface_cast<IBuffer>(buffer);
|
||||||
|
const DmaBuffer *dmabuf = static_cast<const DmaBuffer*>(iBuffer->getClientData());
|
||||||
|
|
||||||
|
return const_cast<DmaBuffer*>(dmabuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsumerThread::ConsumerThread(OutputStream* stream) :
|
||||||
|
m_stream(stream),
|
||||||
|
m_enc_ctx(NULL),
|
||||||
|
m_frame(NULL),
|
||||||
|
m_pkt(NULL),
|
||||||
|
m_outputFile(NULL),
|
||||||
|
m_gotError(false),
|
||||||
|
m_blitSurf(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsumerThread::~ConsumerThread()
|
||||||
|
{
|
||||||
|
if (m_outputFile)
|
||||||
|
delete m_outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConsumerThread::threadInitialize()
|
||||||
|
{
|
||||||
|
/* Create Video Encoder */
|
||||||
|
if (!createVideoEncoder())
|
||||||
|
ORIGINATE_ERROR("Failed to create video VideoEncoder");
|
||||||
|
|
||||||
|
/* Create output file */
|
||||||
|
m_outputFile = new std::ofstream(OUTPUT_FILENAME.c_str());
|
||||||
|
if (!m_outputFile)
|
||||||
|
ORIGINATE_ERROR("Failed to open output file.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConsumerThread::threadExecute()
|
||||||
|
{
|
||||||
|
IBufferOutputStream* stream = interface_cast<IBufferOutputStream>(m_stream);
|
||||||
|
DmaBuffer *dmabuf = NULL;
|
||||||
|
Argus::Status status = STATUS_OK;
|
||||||
|
if (!stream)
|
||||||
|
ORIGINATE_ERROR("Failed to get IBufferOutputStream interface");
|
||||||
|
|
||||||
|
for (int bufferIndex = 0; bufferIndex < MAX_ENCODER_FRAMES; bufferIndex++)
|
||||||
|
{
|
||||||
|
Buffer* buffer = stream->acquireBuffer(TIMEOUT_INFINITE, &status);
|
||||||
|
if (status != STATUS_OK)
|
||||||
|
{
|
||||||
|
m_gotError = true;
|
||||||
|
ORIGINATE_ERROR("Failed to acquire camera buffer");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Convert Argus::Buffer to DmaBuffer*/
|
||||||
|
dmabuf = DmaBuffer::fromArgusBuffer(buffer);
|
||||||
|
|
||||||
|
int dmabuf_fd = dmabuf->getFd();
|
||||||
|
if (VERBOSE_ENABLE)
|
||||||
|
CONSUMER_PRINT("Acquired Frame:%d fd:%d\n", m_count, dmabuf_fd);
|
||||||
|
/* encode the image */
|
||||||
|
libavEncode(m_enc_ctx, m_frame, m_pkt, dmabuf_fd);
|
||||||
|
|
||||||
|
/* Release the frame */
|
||||||
|
stream->releaseBuffer(buffer);
|
||||||
|
if (VERBOSE_ENABLE)
|
||||||
|
CONSUMER_PRINT("Released frame:%d fd:%d\n", m_count - 1, dmabuf->getFd());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep acquire frames and queue into encoder */
|
||||||
|
while (!m_gotError)
|
||||||
|
{
|
||||||
|
/* Acquire a Buffer from a completed capture request */
|
||||||
|
Buffer* buffer = stream->acquireBuffer(TIMEOUT_INFINITE, &status);
|
||||||
|
if (status == STATUS_END_OF_STREAM)
|
||||||
|
{
|
||||||
|
/* Timeout or error happen, exit */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (status != STATUS_OK)
|
||||||
|
{
|
||||||
|
ORIGINATE_ERROR("Failed to acquire camera buffer");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert Argus::Buffer to DmaBuffer and get FD */
|
||||||
|
dmabuf = DmaBuffer::fromArgusBuffer(buffer);
|
||||||
|
int dmabuf_fd = dmabuf->getFd();
|
||||||
|
|
||||||
|
if (VERBOSE_ENABLE)
|
||||||
|
CONSUMER_PRINT("Acquired Frame:%d fd:%d\n", m_count, dmabuf_fd);
|
||||||
|
|
||||||
|
/* Push the frame to libavEncoder. */
|
||||||
|
CHECK_ERROR(libavEncode(m_enc_ctx, m_frame, m_pkt, dmabuf_fd));
|
||||||
|
|
||||||
|
stream->releaseBuffer(buffer);
|
||||||
|
if (VERBOSE_ENABLE)
|
||||||
|
CONSUMER_PRINT("Released frame:%d fd:%d\n", m_count - 1, dmabuf->getFd());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send EOS and flush the encoder */
|
||||||
|
CHECK_ERROR(libavEncode(m_enc_ctx, NULL, m_pkt, -1));
|
||||||
|
|
||||||
|
CONSUMER_PRINT("Done.\n");
|
||||||
|
|
||||||
|
requestShutdown();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConsumerThread::threadShutdown()
|
||||||
|
{
|
||||||
|
/* Destroy Video Encoder */
|
||||||
|
if (!destroyVideoEncoder())
|
||||||
|
ORIGINATE_ERROR("Failed to destroy video VideoEncoder");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConsumerThread::createVideoEncoder()
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
NvBufSurfaceAllocateParams input_params = { 0 };
|
||||||
|
|
||||||
|
codec = avcodec_find_encoder_by_name("libx264");
|
||||||
|
if (!codec)
|
||||||
|
ORIGINATE_ERROR("Codec 'libx264' not found. Please install x264");
|
||||||
|
|
||||||
|
m_enc_ctx = avcodec_alloc_context3(codec);
|
||||||
|
if (!m_enc_ctx)
|
||||||
|
ORIGINATE_ERROR("Could not allocate video codec context");
|
||||||
|
|
||||||
|
m_count = 0;
|
||||||
|
// Setting common properties of encoder available as part of AVCodecContext
|
||||||
|
// Ref: https://libav.org/documentation/doxygen/master/structAVCodecContext.html
|
||||||
|
|
||||||
|
/* put sample parameters */
|
||||||
|
m_enc_ctx->bit_rate = 4000000;
|
||||||
|
m_enc_ctx->width = STREAM_SIZE.width();
|
||||||
|
m_enc_ctx->height = STREAM_SIZE.height();
|
||||||
|
/* frames per second */
|
||||||
|
m_enc_ctx->time_base = (AVRational){1, 30};
|
||||||
|
m_enc_ctx->framerate = (AVRational){30, 1};
|
||||||
|
m_enc_ctx->gop_size = 30;
|
||||||
|
m_enc_ctx->max_b_frames = 0;
|
||||||
|
m_enc_ctx->pix_fmt = (ENCODER_INPIXFMT == NV12) ? AV_PIX_FMT_NV12 : AV_PIX_FMT_YUV420P;
|
||||||
|
m_enc_ctx->profile = FF_PROFILE_H264_HIGH;
|
||||||
|
|
||||||
|
// Setting some specific properties of libx264
|
||||||
|
// Ref: http://www.chaneru.com/Roku/HLS/X264_Settings.htm
|
||||||
|
av_opt_set(m_enc_ctx->priv_data, "level", "5", 0);
|
||||||
|
av_opt_set(m_enc_ctx->priv_data, "preset", "ultrafast", 0);
|
||||||
|
// Set CBR mode in libx264
|
||||||
|
av_opt_set(m_enc_ctx->priv_data, "bitrate", "4000000", 0);
|
||||||
|
av_opt_set(m_enc_ctx->priv_data, "vbv-maxrate", "4000000", 0);
|
||||||
|
av_opt_set(m_enc_ctx->priv_data, "vbv-bufsize", "4000000", 0);
|
||||||
|
// Set num of reference to 1
|
||||||
|
av_opt_set(m_enc_ctx->priv_data, "ref", "1", 0);
|
||||||
|
|
||||||
|
/* open the encoder */
|
||||||
|
ret = avcodec_open2(m_enc_ctx, codec, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
ORIGINATE_ERROR("Could not open libx264 encoder");
|
||||||
|
|
||||||
|
m_frame = av_frame_alloc();
|
||||||
|
if (!m_frame)
|
||||||
|
ORIGINATE_ERROR("Could not allocate video frame");
|
||||||
|
|
||||||
|
m_frame->format = m_enc_ctx->pix_fmt; // Camera input is NV12 format by default
|
||||||
|
m_frame->width = STREAM_SIZE.width();
|
||||||
|
m_frame->height = STREAM_SIZE.height();
|
||||||
|
|
||||||
|
ret = av_frame_get_buffer(m_frame, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
ORIGINATE_ERROR("Could not allocate the video frame data");
|
||||||
|
|
||||||
|
m_pkt = av_packet_alloc();
|
||||||
|
if (!m_pkt)
|
||||||
|
ORIGINATE_ERROR("Could not allocate video packet");
|
||||||
|
|
||||||
|
if(m_frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
{
|
||||||
|
input_params.params.width = m_frame->width;
|
||||||
|
input_params.params.height = m_frame->height;
|
||||||
|
input_params.params.layout = NVBUF_LAYOUT_BLOCK_LINEAR;
|
||||||
|
input_params.params.colorFormat = NVBUF_COLOR_FORMAT_YUV420;
|
||||||
|
input_params.params.memType = NVBUF_MEM_SURFACE_ARRAY;
|
||||||
|
input_params.memtag = NvBufSurfaceTag_VIDEO_CONVERT;
|
||||||
|
ret = NvBufSurfaceAllocate(&m_blitSurf, 1, &input_params);
|
||||||
|
if (ret < 0)
|
||||||
|
ORIGINATE_ERROR("Could not allocate the yuv420p surf data");
|
||||||
|
m_blitSurf->numFilled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&input_params, 0, sizeof(input_params));
|
||||||
|
input_params.params.width = m_frame->width;
|
||||||
|
input_params.params.height = m_frame->height;
|
||||||
|
if(m_frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
input_params.params.colorFormat = NVBUF_COLOR_FORMAT_YUV420;
|
||||||
|
else
|
||||||
|
input_params.params.colorFormat = NVBUF_COLOR_FORMAT_NV12;
|
||||||
|
input_params.params.layout = NVBUF_LAYOUT_PITCH;
|
||||||
|
input_params.params.memType = NVBUF_MEM_SYSTEM;
|
||||||
|
input_params.memtag = NvBufSurfaceTag_NONE;
|
||||||
|
input_params.disablePitchPadding = true;
|
||||||
|
//Allocate system memory to perform copy from HW to SW
|
||||||
|
m_sysBuffers = (NvBufSurface**) malloc(NUM_BUFFERS * sizeof(NvBufSurface*));
|
||||||
|
for (int i = 0; i < NUM_BUFFERS; i++)
|
||||||
|
{
|
||||||
|
ret = NvBufSurfaceAllocate(&m_sysBuffers[i], 1, &input_params);
|
||||||
|
if (ret < 0)
|
||||||
|
ORIGINATE_ERROR("Could not allocate the system memory buffer");
|
||||||
|
m_sysBuffers[i]->numFilled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConsumerThread::libavEncode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, int fd)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
NvBufSurface *surf;
|
||||||
|
int nplanes = 2;
|
||||||
|
int width = 0, height = 0, size = 0;
|
||||||
|
|
||||||
|
if (frame)
|
||||||
|
{
|
||||||
|
av_init_packet(pkt);
|
||||||
|
pkt->data = NULL; // packet data will be allocated by the encoder
|
||||||
|
pkt->size = 0;
|
||||||
|
/* make sure the frame data is writable */
|
||||||
|
ret = av_frame_make_writable(frame);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "AV Frame is not writable\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = NvBufSurfaceFromFd(fd, (void**)&surf);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
ORIGINATE_ERROR("%s: NvBufSurfaceFromFd failed", __func__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: An additional Transform is required if input pix_fmt is I420
|
||||||
|
// Conversion to NV12 -> I420 is performed using VIC.
|
||||||
|
if (frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
{
|
||||||
|
nplanes = 3;
|
||||||
|
NvBufSurfTransformRect src_rect, dest_rect;
|
||||||
|
src_rect.top = dest_rect.top = 0;
|
||||||
|
src_rect.left = dest_rect.left = 0;
|
||||||
|
src_rect.width = dest_rect.width = frame->width;
|
||||||
|
src_rect.height = dest_rect.height = frame->height;
|
||||||
|
|
||||||
|
NvBufSurfTransformParams transform_params;
|
||||||
|
memset(&transform_params,0,sizeof (transform_params));
|
||||||
|
|
||||||
|
transform_params.transform_flag = NVBUFSURF_TRANSFORM_FILTER;
|
||||||
|
transform_params.transform_flip = NvBufSurfTransform_None;
|
||||||
|
transform_params.transform_filter = NvBufSurfTransformInter_Algo3;
|
||||||
|
transform_params.src_rect = &src_rect;
|
||||||
|
transform_params.dst_rect = &dest_rect;
|
||||||
|
ret = NvBufSurfTransform(surf, m_blitSurf, &transform_params);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ORIGINATE_ERROR("Could not transform nv12 to yuv420p surf data");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: A HW to SW copy is required due to alignment contraints
|
||||||
|
// The given input is HW buffer. Call NvBufSurfaceCopy to copy to raw AVFrame buffer.
|
||||||
|
ret = NvBufSurfaceCopy((nplanes == 2 ? surf : m_blitSurf), m_sysBuffers[m_count % NUM_BUFFERS]);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ORIGINATE_ERROR("NvBufSurfaceCopy Failed");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *data = (uint8_t *)m_sysBuffers[m_count % NUM_BUFFERS]->surfaceList[0].dataPtr;
|
||||||
|
// Set the avframe data
|
||||||
|
for (int plane = 0; plane < nplanes; plane++)
|
||||||
|
{
|
||||||
|
frame->data[plane] = (uint8_t *)(data + size);
|
||||||
|
frame->linesize[plane] = m_sysBuffers[m_count % NUM_BUFFERS]->surfaceList[0].planeParams.pitch[plane];
|
||||||
|
size += m_sysBuffers[m_count % NUM_BUFFERS]->surfaceList[0].planeParams.psize[plane];
|
||||||
|
}
|
||||||
|
|
||||||
|
frame->pts = m_count;
|
||||||
|
m_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = avcodec_send_frame(m_enc_ctx, frame);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Error while encoding frame\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOS:
|
||||||
|
ret = avcodec_receive_packet(m_enc_ctx, pkt);
|
||||||
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||||||
|
return 0;
|
||||||
|
else if (ret < 0)
|
||||||
|
{
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Error while encoding frame\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkt->size > 0)
|
||||||
|
{
|
||||||
|
this->m_outputFile->write((char *) pkt->data, pkt->size);
|
||||||
|
av_packet_unref(pkt);
|
||||||
|
// Loop back to retrieve remaining encoded packets
|
||||||
|
if (frame == NULL)
|
||||||
|
goto EOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConsumerThread::destroyVideoEncoder()
|
||||||
|
{
|
||||||
|
if (m_frame && m_frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
{
|
||||||
|
NvBufSurfaceDestroy(m_blitSurf);
|
||||||
|
m_blitSurf = NULL;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < NUM_BUFFERS; i++)
|
||||||
|
{
|
||||||
|
NvBufSurfaceDestroy(m_sysBuffers[i]);
|
||||||
|
m_sysBuffers[i] = NULL;
|
||||||
|
}
|
||||||
|
free(m_sysBuffers);
|
||||||
|
m_sysBuffers = NULL;
|
||||||
|
if (m_pkt)
|
||||||
|
{
|
||||||
|
av_packet_free(&m_pkt);
|
||||||
|
m_pkt = NULL;
|
||||||
|
}
|
||||||
|
if (m_frame)
|
||||||
|
{
|
||||||
|
av_frame_free(&m_frame);
|
||||||
|
m_frame = NULL;
|
||||||
|
}
|
||||||
|
if (m_enc_ctx)
|
||||||
|
{
|
||||||
|
//avcodec_close(&m_enc_ctx);
|
||||||
|
avcodec_free_context(&m_enc_ctx);
|
||||||
|
m_enc_ctx = NULL;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsumerThread::abort()
|
||||||
|
{
|
||||||
|
destroyVideoEncoder();
|
||||||
|
m_gotError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArgusSamples::execute()
|
||||||
|
{
|
||||||
|
NvBufSurface *surf[NUM_BUFFERS] = {0};
|
||||||
|
|
||||||
|
/* Create the CameraProvider object and get the core interface */
|
||||||
|
UniqueObj<CameraProvider> cameraProvider = UniqueObj<CameraProvider>(CameraProvider::create());
|
||||||
|
ICameraProvider *iCameraProvider = interface_cast<ICameraProvider>(cameraProvider);
|
||||||
|
if (!iCameraProvider)
|
||||||
|
ORIGINATE_ERROR("Failed to create CameraProvider");
|
||||||
|
|
||||||
|
/* Get the camera devices */
|
||||||
|
std::vector<CameraDevice*> cameraDevices;
|
||||||
|
iCameraProvider->getCameraDevices(&cameraDevices);
|
||||||
|
if (cameraDevices.size() == 0)
|
||||||
|
ORIGINATE_ERROR("No cameras available");
|
||||||
|
|
||||||
|
if (CAMERA_INDEX >= cameraDevices.size())
|
||||||
|
{
|
||||||
|
PRODUCER_PRINT("CAMERA_INDEX out of range. Fall back to 0\n");
|
||||||
|
CAMERA_INDEX = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the capture session using the first device and get the core interface */
|
||||||
|
UniqueObj<CaptureSession> captureSession(
|
||||||
|
iCameraProvider->createCaptureSession(cameraDevices[CAMERA_INDEX]));
|
||||||
|
ICaptureSession *iCaptureSession = interface_cast<ICaptureSession>(captureSession);
|
||||||
|
if (!iCaptureSession)
|
||||||
|
ORIGINATE_ERROR("Failed to get ICaptureSession interface");
|
||||||
|
|
||||||
|
/* Create the OutputStream */
|
||||||
|
PRODUCER_PRINT("Creating output stream\n");
|
||||||
|
UniqueObj<OutputStreamSettings> streamSettings(
|
||||||
|
iCaptureSession->createOutputStreamSettings(STREAM_TYPE_BUFFER));
|
||||||
|
IBufferOutputStreamSettings *iStreamSettings =
|
||||||
|
interface_cast<IBufferOutputStreamSettings>(streamSettings);
|
||||||
|
if (!iStreamSettings)
|
||||||
|
ORIGINATE_ERROR("Failed to get IBufferOutputStreamSettings interface");
|
||||||
|
|
||||||
|
/* Configure the OutputStream to use the EGLImage BufferType */
|
||||||
|
iStreamSettings->setBufferType(BUFFER_TYPE_EGL_IMAGE);
|
||||||
|
|
||||||
|
/* Create the OutputStream */
|
||||||
|
UniqueObj<OutputStream> outputStream(iCaptureSession->createOutputStream(streamSettings.get()));
|
||||||
|
IBufferOutputStream *iBufferOutputStream = interface_cast<IBufferOutputStream>(outputStream);
|
||||||
|
|
||||||
|
/* Allocate native buffers */
|
||||||
|
DmaBuffer* nativeBuffers[NUM_BUFFERS];
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < NUM_BUFFERS; i++)
|
||||||
|
{
|
||||||
|
nativeBuffers[i] = DmaBuffer::create(STREAM_SIZE, NVBUF_COLOR_FORMAT_NV12,
|
||||||
|
NVBUF_LAYOUT_BLOCK_LINEAR);
|
||||||
|
if (!nativeBuffers[i])
|
||||||
|
ORIGINATE_ERROR("Failed to allocate NativeBuffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create EGLImages from the native buffers */
|
||||||
|
EGLImageKHR eglImages[NUM_BUFFERS];
|
||||||
|
for (uint32_t i = 0; i < NUM_BUFFERS; i++)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = NvBufSurfaceFromFd(nativeBuffers[i]->getFd(), (void**)(&surf[i]));
|
||||||
|
if (ret)
|
||||||
|
ORIGINATE_ERROR("%s: NvBufSurfaceFromFd failed", __func__);
|
||||||
|
|
||||||
|
ret = NvBufSurfaceMapEglImage (surf[i], 0);
|
||||||
|
if (ret)
|
||||||
|
ORIGINATE_ERROR("%s: NvBufSurfaceMapEglImage failed", __func__);
|
||||||
|
|
||||||
|
eglImages[i] = surf[i]->surfaceList[0].mappedAddr.eglImage;
|
||||||
|
if (eglImages[i] == EGL_NO_IMAGE_KHR)
|
||||||
|
ORIGINATE_ERROR("Failed to create EGLImage");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the BufferSettings object to configure Buffer creation */
|
||||||
|
UniqueObj<BufferSettings> bufferSettings(iBufferOutputStream->createBufferSettings());
|
||||||
|
IEGLImageBufferSettings *iBufferSettings =
|
||||||
|
interface_cast<IEGLImageBufferSettings>(bufferSettings);
|
||||||
|
if (!iBufferSettings)
|
||||||
|
ORIGINATE_ERROR("Failed to create BufferSettings");
|
||||||
|
|
||||||
|
/* Create the Buffers for each EGLImage (and release to
|
||||||
|
stream for initial capture use) */
|
||||||
|
UniqueObj<Buffer> buffers[NUM_BUFFERS];
|
||||||
|
for (uint32_t i = 0; i < NUM_BUFFERS; i++)
|
||||||
|
{
|
||||||
|
iBufferSettings->setEGLImage(eglImages[i]);
|
||||||
|
iBufferSettings->setEGLDisplay(eglDisplay);
|
||||||
|
buffers[i].reset(iBufferOutputStream->createBuffer(bufferSettings.get()));
|
||||||
|
IBuffer *iBuffer = interface_cast<IBuffer>(buffers[i]);
|
||||||
|
|
||||||
|
/* Reference Argus::Buffer and DmaBuffer each other */
|
||||||
|
iBuffer->setClientData(nativeBuffers[i]);
|
||||||
|
nativeBuffers[i]->setArgusBuffer(buffers[i].get());
|
||||||
|
|
||||||
|
if (!interface_cast<IEGLImageBuffer>(buffers[i]))
|
||||||
|
ORIGINATE_ERROR("Failed to create Buffer");
|
||||||
|
if (iBufferOutputStream->releaseBuffer(buffers[i].get()) != STATUS_OK)
|
||||||
|
ORIGINATE_ERROR("Failed to release Buffer for capture use");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Launch the FrameConsumer thread to consume frames from the OutputStream */
|
||||||
|
PRODUCER_PRINT("Launching consumer thread\n");
|
||||||
|
ConsumerThread frameConsumerThread(outputStream.get());
|
||||||
|
PROPAGATE_ERROR(frameConsumerThread.initialize());
|
||||||
|
|
||||||
|
/* Wait until the consumer is connected to the stream */
|
||||||
|
PROPAGATE_ERROR(frameConsumerThread.waitRunning());
|
||||||
|
|
||||||
|
/* Create capture request and enable output stream */
|
||||||
|
UniqueObj<Request> request(iCaptureSession->createRequest());
|
||||||
|
IRequest *iRequest = interface_cast<IRequest>(request);
|
||||||
|
if (!iRequest)
|
||||||
|
ORIGINATE_ERROR("Failed to create Request");
|
||||||
|
iRequest->enableOutputStream(outputStream.get());
|
||||||
|
|
||||||
|
ISourceSettings *iSourceSettings = interface_cast<ISourceSettings>(iRequest->getSourceSettings());
|
||||||
|
if (!iSourceSettings)
|
||||||
|
ORIGINATE_ERROR("Failed to get ISourceSettings interface");
|
||||||
|
iSourceSettings->setFrameDurationRange(Range<uint64_t>(1e9/DEFAULT_FPS));
|
||||||
|
|
||||||
|
/* Submit capture requests */
|
||||||
|
PRODUCER_PRINT("Starting repeat capture requests.\n");
|
||||||
|
if (iCaptureSession->repeat(request.get()) != STATUS_OK)
|
||||||
|
ORIGINATE_ERROR("Failed to start repeat capture request");
|
||||||
|
|
||||||
|
/* Wait for CAPTURE_TIME seconds */
|
||||||
|
for (int i = 0; i < CAPTURE_TIME && !frameConsumerThread.isInError(); i++)
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
/* Stop the repeating request and wait for idle */
|
||||||
|
iCaptureSession->stopRepeat();
|
||||||
|
iBufferOutputStream->endOfStream();
|
||||||
|
iCaptureSession->waitForIdle();
|
||||||
|
|
||||||
|
/* Wait for the consumer thread to complete */
|
||||||
|
PROPAGATE_ERROR(frameConsumerThread.shutdown());
|
||||||
|
|
||||||
|
/* Destroy the output stream to end the consumer thread */
|
||||||
|
outputStream.reset();
|
||||||
|
|
||||||
|
/* Destroy the EGLImages */
|
||||||
|
for (uint32_t i = 0; i < NUM_BUFFERS; i++)
|
||||||
|
NvBufSurfaceUnMapEglImage (surf[i], 0);
|
||||||
|
|
||||||
|
/* Destroy the native buffers */
|
||||||
|
for (uint32_t i = 0; i < NUM_BUFFERS; i++)
|
||||||
|
delete nativeBuffers[i];
|
||||||
|
|
||||||
|
PRODUCER_PRINT("Done -- exiting.\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printHelp()
|
||||||
|
{
|
||||||
|
printf("Usage: camera_recording [OPTIONS]\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -r Set output resolution WxH [Default 640x480]\n"
|
||||||
|
" -f Set output filename [Default output.h264]\n"
|
||||||
|
" -d Set capture duration [Default 5 seconds]\n"
|
||||||
|
" -i Set camera index [Default 0]\n"
|
||||||
|
" -v Enable verbose message\n"
|
||||||
|
" -p Encoder input format 1 - NV12 [Default] 2 - I420\n"
|
||||||
|
" -h Print this help\n"
|
||||||
|
"Note: Only H.264 format is supported for SW encoding.\n"
|
||||||
|
"If encoder input format I420 is selected, additional transform to convert input to I420 is done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseCmdline(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int c, w, h;
|
||||||
|
bool haveFilename = false;
|
||||||
|
while ((c = getopt(argc, argv, "r:f:t:d:i:p:s::v::h")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'r':
|
||||||
|
if (sscanf(optarg, "%dx%d", &w, &h) != 2)
|
||||||
|
return false;
|
||||||
|
STREAM_SIZE.width() = w;
|
||||||
|
STREAM_SIZE.height() = h;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
OUTPUT_FILENAME = optarg;
|
||||||
|
haveFilename = true;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
CAPTURE_TIME = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
CAMERA_INDEX = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
VERBOSE_ENABLE = true;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
if (2 == atoi(optarg))
|
||||||
|
ENCODER_INPIXFMT = I420;
|
||||||
|
else
|
||||||
|
ENCODER_INPIXFMT = NV12;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (!parseCmdline(argc, argv))
|
||||||
|
{
|
||||||
|
printHelp();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get default EGL display */
|
||||||
|
eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||||
|
if (eglDisplay == EGL_NO_DISPLAY)
|
||||||
|
{
|
||||||
|
printf("Cannot get EGL display.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ArgusSamples::execute())
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
/* Terminate EGL display */
|
||||||
|
eglTerminate(eglDisplay);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
6
commitFile.txt
Normal file
6
commitFile.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Updating prebuilts and/or headers
|
||||||
|
|
||||||
|
7d8cfb5596b00c49ca01c4d59ee74c8b88059909 - 19_argus_camera_sw_encode/Makefile
|
||||||
|
0237b5299525c072e55e721dcedbfd3d06e182ca - 19_argus_camera_sw_encode/FFMPEG-LICENSE.md
|
||||||
|
de91caa771262821be9e97cfe54154efd19b3741 - 19_argus_camera_sw_encode/argus_camera_sw_encode_main.cpp
|
||||||
|
7b299759a546d9c7ed36d7ab0db92bcd15eb5892 - 19_argus_camera_sw_encode/argus_camera_sw_encode.h
|
||||||
1
push_info.txt
Normal file
1
push_info.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
jetson_38.2.1
|
||||||
Reference in New Issue
Block a user