├── .gitignore
├── .idea
├── .gitignore
└── misc.xml
├── CMakeLists.txt
├── LICENSE
├── README.md
├── ReaderDemo.cc
├── VideoReader.cc
├── VideoReader.h
├── VideoWriter.cc
├── VideoWriter.h
├── WriterDemo.cc
├── image
├── decode_flow.png
└── encode_flow.png
└── input
└── input.mp4
/.gitignore:
--------------------------------------------------------------------------------
1 | build*
2 | .vscode
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.6)
2 | PROJECT(sample)
3 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ./bin/)
4 | find_package(OpenCV required)
5 | # include(/home/jungle/smm/dependencies/opencv/qualcomm/lib/cmake/opencv4/OpenCVConfig.cmake)
6 |
7 | # 添加reader的demo
8 | add_executable(VideoReaderDome ReaderDemo.cc VideoReader.cc VideoReader.h)
9 | # target_link_directories(/home/jungle/smm/dependencies/opencv/qualcomm/lib)
10 | # target_link_libraries(VideoReaderDome PUBLIC /home/jungle/smm/dependencies/opencv/qualcomm/lib/libopencv_img_hash.so /home/jungle/smm/dependencies/opencv/qualcomm/lib/libopencv_img_hash.so.4.1 /home/jungle/smm/dependencies/opencv/qualcomm/lib/libopencv_img_hash.so.4.1.0 /home/jungle/smm/dependencies/opencv/qualcomm/lib/libopencv_world.so /home/jungle/smm/dependencies/opencv/qualcomm/lib/libopencv_world.so.4.1 /home/jungle/smm/dependencies/opencv/qualcomm/lib/libopencv_world.so.4.1.0)
11 | target_link_libraries(VideoReaderDome PUBLIC ${OpenCV_LIBS})
12 | target_link_libraries(VideoReaderDome PUBLIC "-lgstreamer-1.0 -lgobject-2.0 -lglib-2.0")
13 | target_include_directories(VideoReaderDome PUBLIC /usr/include/gstreamer-1.0)
14 | target_include_directories(VideoReaderDome PUBLIC /usr/include/gobject-2.0)
15 | target_include_directories(VideoReaderDome PUBLIC /usr/include/glib-2.0)
16 | target_include_directories(VideoReaderDome PUBLIC ${OpenCV_INCLUDE_DIRS})
17 |
18 | # 添加writer的demo
19 | add_executable(VideoWriterDome WriterDemo.cc VideoWriter.cc VideoWriter.h VideoReader.cc VideoReader.h)
20 | target_link_libraries(VideoWriterDome PUBLIC ${OpenCV_LIBS})
21 | target_link_libraries(VideoWriterDome PUBLIC "-lgstreamer-1.0 -lgobject-2.0 -lglib-2.0")
22 | target_include_directories(VideoWriterDome PUBLIC /usr/include/gstreamer-1.0)
23 | target_include_directories(VideoWriterDome PUBLIC /usr/include/gobject-2.0)
24 | target_include_directories(VideoWriterDome PUBLIC /usr/include/glib-2.0)
25 | target_include_directories(VideoWriterDome PUBLIC ${OpenCV_INCLUDE_DIRS})
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 介绍
2 | * 这个项目是一个使用`gstreamer`库在高通的`RB5`平台编译运行的项目
3 | * 读视频每一帧:使用了`appsink`从管道中取出数据传到管道外的`cv::Mat`
4 | 
5 | * 写视频每一帧:使用了appsrc将数据输入管道,并且使用filesink接受数据
6 | 
7 |
8 | ## 运行
9 | * 需要在linux-ubuntu下运行,安装gstreamer可参考[gstreamer docs](https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c)
10 | ```
11 | apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio
12 | ```
13 | * 运行该项目可以参考如下命令
14 | ```
15 | mkdir build
16 | cd build
17 | cmake ..
18 | make -j4
19 | cd bin
20 | # 你应该将input.mp4拷贝到根目录下的input文件夹中
21 | ./VideoReaderDome ../../input/input.mp4 ../../input
22 | ./VideoWriterDome ../../input/input.mp4 ../../input/output.mp4
23 | ```
24 | ## 参考
25 | 有关参考请看
26 | * [【gstreamer opencv::Mat】使用gstreamer读取视频中的每一帧为cv::Mat](https://blog.csdn.net/weixin_44495869/article/details/121898322)
27 | * [【gstreamer opencv::Mat】将opencv的cv::Mat数据转换成MP4视频](https://blog.csdn.net/weixin_44495869/article/details/121900517)
28 | * [【gstreamer中appsink和appsrc操作数据转换cv::Mat】参考文献](https://blog.csdn.net/weixin_44495869/article/details/121902501)
--------------------------------------------------------------------------------
/ReaderDemo.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include "VideoReader.h"
3 | #include
4 |
5 | void TestVideoReader(std::string url, std::string outUrl, int count) {
6 | std::cout << "video:" << url << std::endl;
7 |
8 | VideoReader video;
9 | // 需要输入原视频尺寸
10 | video.InputOriginSize(1920,1080);
11 |
12 | auto ret = video.Open(url);
13 | if (ret < 0) return;
14 |
15 | cv::Mat frame;
16 | int seq = 0;
17 | double timestamp = .0;
18 |
19 | while (seq++ < count) {
20 | std::cout << "reading " << seq << "th loop." << std::endl;
21 |
22 | auto ret = video.Read(frame, timestamp);
23 | if (ret < 0) break;
24 |
25 | std::string filename = outUrl + "/" + std::to_string(seq) + ".jpg";
26 | cv::imwrite(filename, frame);
27 | }
28 |
29 | std::cout << "video read over" << std::endl;
30 | }
31 |
32 |
33 | int main(int argc, char* argv[]) {
34 | gst_init(&argc, &argv);
35 |
36 | std::string inputUrl(argv[1]);
37 | std::string outputUrl(argv[2]);
38 |
39 | std::cout << "read video:" << inputUrl << std::endl;
40 |
41 | TestVideoReader(inputUrl, outputUrl, 50);
42 |
43 | return 0;
44 | }
45 |
--------------------------------------------------------------------------------
/VideoReader.cc:
--------------------------------------------------------------------------------
1 | #include "VideoReader.h"
2 | #include
3 |
4 | static inline void QtdemuxPadAddedCb(GstElement *qtdemux, GstPad *pad, GstElement *queue) {
5 | gst_element_link_pads(qtdemux, GST_PAD_NAME(pad), queue, nullptr);
6 | }
7 |
8 | // 错误或EOS流结束处理函数,因为会阻塞线程,不用它
9 | static inline void ErrHandle(GstElement *pipeline) {
10 | // Wait until error or EOS
11 | auto bus = gst_element_get_bus(pipeline);
12 | auto msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
13 |
14 | // Message handling
15 | if (msg != nullptr) {
16 | switch (GST_MESSAGE_TYPE(msg)) {
17 | case GST_MESSAGE_ERROR: {
18 | GError *err = nullptr;
19 | gchar *debug_info = nullptr;
20 | gst_message_parse_error(msg, &err, &debug_info);
21 | std::cerr<< "Error received:" << err->message << std::endl;
22 | if (debug_info) {
23 | std::cerr<< "Debugging information:" << debug_info << std::endl;
24 | }
25 | g_clear_error(&err);
26 | g_free(debug_info);
27 | }
28 | break;
29 | case GST_MESSAGE_EOS:
30 | std::cout << "End-Of-Stream reached" << std::endl;
31 | break;
32 | default:
33 | std::cout << "Unexpected message received" << std::endl;
34 | break;
35 | }
36 | gst_message_unref(msg);
37 | }
38 |
39 | // Free resources
40 | gst_object_unref(bus);
41 | }
42 |
43 | // 主要功能:将pipeline中的数据拷贝到cv:Mat
44 | int VideoReader::RecvDecodedFrame(cv::Mat& frame, double& timestamp) {
45 | GstSample *sample;
46 | // 使用pull-sample拉取视频帧,并映射到map变量,通过map拷贝出frame数据
47 | g_signal_emit_by_name(sink_, "pull-sample", &sample);
48 | if (sample) {
49 | auto buffer = gst_sample_get_buffer(sample);
50 |
51 | // fetch timestamp
52 | timestamp = static_cast(GST_BUFFER_PTS(buffer)) / static_cast(GST_SECOND);
53 | // std::cout << "timestamp:" << timestamp << std::endl;
54 |
55 | // copy buffer data into cv::Mat
56 | GstMapInfo map;
57 | if (gst_buffer_map(buffer, &map, GST_MAP_READ)) {
58 | std::cout << "recv data size:" << map.size << std::endl;
59 | if (srcFmt_ != "NV12" && srcFmt_ != "I420") {
60 | std::cout << "unsupported src pixel format" << std::endl;
61 | return -1;
62 | }
63 | static cv::Mat image;
64 | if (image.empty()) {
65 | image.create(cv::Size(width_, height_ * 3 / 2), CV_8UC1);
66 | }
67 | // 1. copy into cv::Mat
68 | if (paddedWidth_ == width_) {
69 | memcpy(image.data, map.data, width_ * sizeof(uint8_t) * height_);
70 | memcpy(image.data + height_ * width_, map.data + paddedHeight_ * width_, width_ * sizeof(uint8_t) * height_ / 2);
71 | } else {
72 | // copy Y-channel, jump the padding width
73 | for (int i = 0; i < height_; ++i) {
74 | memcpy(image.data + i * width_, map.data + i * paddedWidth_, width_ * sizeof(uint8_t));
75 | }
76 | // copy UV-channel, jump the padding width
77 | for (int i = 0; i < height_ / 2; ++i) {
78 | memcpy(image.data + (height_ + i) * width_, map.data + (paddedHeight_ + i) * paddedWidth_, width_ * sizeof(uint8_t));
79 | }
80 | }
81 |
82 | // 2. convert format
83 | if (srcFmt_ == "NV12") {
84 | cv::cvtColor(image, frame, cv::COLOR_YUV2BGR_NV12);
85 | } else {
86 | cv::cvtColor(image, frame, cv::COLOR_YUV2BGR_I420);
87 | }
88 |
89 | // release buffer mapping
90 | gst_buffer_unmap(buffer, &map);
91 | }
92 | // release sample reference
93 | gst_sample_unref(sample);
94 | return 0;
95 | } else {
96 | std::cerr << "recv null frame" << std::endl;
97 | return -1;
98 | }
99 | }
100 |
101 | int VideoReader::Open(const std::string& url) {
102 | // create the elements
103 | source_ = gst_element_factory_make("filesrc", "InputFile");
104 | qtdemux_ = gst_element_factory_make("qtdemux", "QtDemux");
105 | queue_ = gst_element_factory_make("queue", "QueueReader");
106 | h264parse_ = gst_element_factory_make("h264parse", "H264Parse");
107 | omxh264dec_ = gst_element_factory_make("omxh264dec", "OmxH264Dec");
108 | sink_ = gst_element_factory_make("appsink", "CustomSink");
109 |
110 | pipeline_ = gst_pipeline_new("decode-pipeline");
111 |
112 | if (!pipeline_ || !source_ || !qtdemux_ || !queue_ || !omxh264dec_ || !h264parse_ || !sink_) {
113 | std::cerr<< "Not all elements could be created" << std::endl;
114 | return -1;
115 | }
116 | // Modify element properties
117 | g_object_set(G_OBJECT(source_), "location", url.c_str(), nullptr);
118 | g_object_set(G_OBJECT(sink_), "emit-signals", TRUE, "max-buffers", 1, nullptr);
119 |
120 | // Build the pipeline
121 | gst_bin_add_many(GST_BIN(pipeline_), source_, qtdemux_, queue_, h264parse_, omxh264dec_, sink_, nullptr);
122 |
123 | if (gst_element_link(source_, qtdemux_) != TRUE ) {
124 | std::cerr<< "source and qtdemux could not be linked" << std::endl;
125 | // gst_object_unref(pipeline_);
126 | return -1;
127 | }
128 | if (gst_element_link_many(queue_, h264parse_, omxh264dec_, sink_, nullptr) != TRUE ) {
129 | std::cerr<< "queue, h264parse, omxh264dec, and sink could not be linked" << std::endl;
130 | // gst_object_unref(pipeline);
131 | return -1;
132 | }
133 | // dynamic padded connect between demux and queue
134 | g_signal_connect(qtdemux_, "pad-added", (GCallback)QtdemuxPadAddedCb, queue_);
135 |
136 | GstStateChangeReturn ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);
137 | if (ret == GST_STATE_CHANGE_FAILURE) {
138 | std::cerr<< "Unable to set the pipeline to the paused state" << std::endl;
139 | return -1;
140 | }
141 | GstSample *sample;
142 | g_signal_emit_by_name(sink_, "pull-preroll", &sample);
143 | if (sample) {
144 | //std::cout << "recv frame" << std::endl;
145 | auto buffer = gst_sample_get_buffer(sample);
146 | // fetch video infomation
147 | if (paddedHeight_ == 0 && paddedWidth_ == 0) {
148 | GstCaps *caps = gst_sample_get_caps(sample);
149 | GstStructure* info = gst_caps_get_structure(caps, 0);
150 | gst_structure_get_int(info, "width", &paddedWidth_);
151 | gst_structure_get_int(info, "height", &paddedHeight_);
152 | const char* format = gst_structure_get_string(info, "format");
153 | gst_structure_get_fraction(info, "framerate", &framerate_.first, &framerate_.second);
154 | srcFmt_ = format;
155 |
156 | std::cout << "padded width:" << paddedWidth_ << "padded height:" << paddedHeight_ << std::endl;
157 | std::cout << "format:" << srcFmt_ << std::endl;
158 | std::cout << "framerate num:" << framerate_.first << "framerate den:" << framerate_.second << std::endl;
159 | }
160 | // release sample reference
161 | gst_sample_unref(sample);
162 | }
163 |
164 | // set pipeline to playing
165 | ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);
166 | if (ret == GST_STATE_CHANGE_FAILURE) {
167 | std::cerr<< "Unable to set the pipeline to the playing state" << std::endl;
168 | return -1;
169 | }
170 |
171 | // handle error or EOS. Atention: error handle will block, so don't use it.
172 | // ErrHandle(pipeline_);
173 | return 0;
174 | }
175 |
176 | int VideoReader::Read(cv::Mat &frame, double ×tamp) {
177 | return RecvDecodedFrame(frame, timestamp);
178 | }
179 |
180 | VideoReader::~VideoReader() {
181 | if (pipeline_) {
182 | gst_element_set_state(pipeline_, GST_STATE_NULL);
183 | gst_object_unref(pipeline_);
184 | pipeline_ = nullptr;
185 | }
186 | }
--------------------------------------------------------------------------------
/VideoReader.h:
--------------------------------------------------------------------------------
1 | #ifndef __VIDEO_READER_H__
2 | #define __VIDEO_READER_H__
3 |
4 | #include "opencv2/opencv.hpp"
5 | #include
6 |
7 | class VideoReader {
8 | public:
9 | /**
10 | * @brief 打开视频, url为视频的文件路径或者网络地址
11 | */
12 | int Open(const std::string &url);
13 |
14 | /**
15 | * 读取视频帧,顺序读取,时间戳单位秒
16 | */
17 | int Read(cv::Mat &frame, double ×tamp);
18 |
19 | /**
20 | * @brief 视频帧率
21 | * @param fps = framerate.first/framerate.second
22 | */
23 | std::pair Framerate() {
24 | return framerate_;
25 | }
26 |
27 | /**
28 | * @brief 需要输入原视频宽高, 因为通过gst得到的是对齐后宽高,需要是16的整数倍
29 | */
30 | void InputOriginSize(const int width, const int height) {
31 | width_ = width;
32 | height_ = height;
33 | }
34 |
35 | ~VideoReader();
36 |
37 | private:
38 | // int NextFrame(AVFrame *frame);
39 | int RecvDecodedFrame(cv::Mat& frame, double& timestamp);
40 | GstElement* pipeline_;
41 | GstElement* source_;
42 | GstElement* qtdemux_;
43 | GstElement* queue_;
44 | GstElement* h264parse_;
45 | GstElement* omxh264dec_;
46 | GstElement* sink_;
47 |
48 | std::string srcFmt_;
49 | int paddedWidth_ = 0;
50 | int paddedHeight_ = 0;
51 | int width_ = 0;
52 | int height_ = 0;
53 | std::pair framerate_;
54 | };
55 |
56 | #endif
--------------------------------------------------------------------------------
/VideoWriter.cc:
--------------------------------------------------------------------------------
1 | #include "VideoWriter.h"
2 | #include
3 |
4 | VideoWriter::~VideoWriter() {
5 | if (appSrc_) {
6 | GstFlowReturn retflow;
7 | g_signal_emit_by_name(appSrc_, "end-of-stream", &retflow);
8 | std::cout << "EOS sended. Writing last several frame..." << std::endl;
9 | g_usleep(4000000); // 等待4s,写数据
10 | std::cout << "Writing Done!" << std::endl;
11 | if (retflow != GST_FLOW_OK) {
12 | std::cerr << "We got some error when sending eos!" << std::endl;
13 | }
14 | }
15 | if (pipeline_) {
16 | gst_element_set_state(pipeline_, GST_STATE_NULL);
17 | gst_object_unref(pipeline_);
18 | pipeline_ = nullptr;
19 | }
20 | }
21 |
22 | int VideoWriter::Open(const std::string url) {
23 | appSrc_ = gst_element_factory_make("appsrc", "AppSrc");
24 | queue_ = gst_element_factory_make("queue", "QueueWrite");
25 | videoConvert_ = gst_element_factory_make("videoconvert", "Videoconvert");
26 | encoder_ = gst_element_factory_make("x264enc", "X264enc");
27 | capsFilter_ = gst_element_factory_make("capsfilter", "Capsfilter");
28 | mux_ = gst_element_factory_make("qtmux", "Qtmux");
29 | sink_ = gst_element_factory_make("filesink", "OutputFile");
30 |
31 | // Create the empty pipeline
32 | pipeline_ = gst_pipeline_new("encode-pipeline");
33 |
34 | if (!pipeline_ || !appSrc_ || !queue_ || !videoConvert_ || !encoder_ || !capsFilter_ || !mux_ || ! sink_) {
35 | std::cerr << "Not all elements could be created" << std::endl;
36 | return -1;
37 | }
38 | // 设置 src format
39 | std::string srcFmt = "BGR";
40 |
41 | // Modify element properties
42 | g_object_set(G_OBJECT(appSrc_), "stream-type", 0, "format", GST_FORMAT_TIME, nullptr);
43 | g_object_set(G_OBJECT(appSrc_), "caps", gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, srcFmt.c_str(),
44 | "width", G_TYPE_INT, width_,
45 | "height", G_TYPE_INT, height_,
46 | "framerate", GST_TYPE_FRACTION, framerate_.first, framerate_.second, nullptr), nullptr);
47 |
48 | g_object_set(G_OBJECT(capsFilter_), "caps", gst_caps_new_simple("video/x-h264",
49 | "stream-format", G_TYPE_STRING,"avc",
50 | "profile", G_TYPE_STRING, "main", nullptr), nullptr);
51 |
52 | g_object_set(G_OBJECT(sink_), "location", url.c_str(), nullptr);
53 | // 设置视频码率
54 | g_object_set(G_OBJECT(encoder_), "bitrate", 1000 + bitrate_ / 1000, nullptr);
55 |
56 | // Build the pipeline
57 | gst_bin_add_many(GST_BIN(pipeline_), appSrc_, queue_, videoConvert_, encoder_, capsFilter_, mux_, sink_, nullptr);
58 |
59 | if (gst_element_link_many(appSrc_, queue_, videoConvert_, encoder_, capsFilter_, mux_, sink_, nullptr) != TRUE ) {
60 | std::cerr << "appSrc, queue, videoConvert, encoder, capsFilter, mux and sink could not be linked" << std::endl;
61 | return -1;
62 | }
63 |
64 | // Start playing
65 | auto ret = gst_element_set_state (pipeline_, GST_STATE_PLAYING);
66 | if (ret == GST_STATE_CHANGE_FAILURE) {
67 | std::cerr << "Unable to set the pipeline to the playing state" << std::endl;
68 | return -1;
69 | }
70 |
71 | return 0;
72 | }
73 |
74 | int VideoWriter::PushData2Pipeline(const cv::Mat& frame, double timestamp) {
75 | GstBuffer *buffer;
76 | GstFlowReturn ret;
77 | GstMapInfo map;
78 |
79 | // Create a new empty buffer
80 | uint size = frame.total() * frame.elemSize();
81 | buffer = gst_buffer_new_and_alloc(size);
82 |
83 | gst_buffer_map(buffer, &map, GST_MAP_WRITE);
84 | memcpy(map.data, frame.data, size);
85 |
86 | // debug
87 | std::cout << "wrote size:" << size << std::endl;
88 |
89 | // 必须写入时间戳和每帧画面持续时间
90 | gst_buffer_unmap(buffer, &map);
91 | GST_BUFFER_PTS(buffer) = static_cast(timestamp * GST_SECOND);
92 | GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, framerate_.first / framerate_.second);
93 |
94 | std::cout << "send data into buffer" << std::endl;
95 | std::cout << "GST_BUFFER_DURATION(buffer):" << GST_BUFFER_DURATION(buffer) << std::endl;
96 | std::cout << "timestamp:" << static_cast(timestamp * GST_SECOND) << std::endl;
97 |
98 |
99 | // Push the buffer into the appsrc
100 | g_signal_emit_by_name(appSrc_, "push-buffer", buffer, &ret);
101 |
102 | // Free the buffer now that we are done with it
103 | gst_buffer_unref(buffer);
104 |
105 | if (ret != GST_FLOW_OK) {
106 | // We got some error, stop sending data
107 | std::cout << "We got some error, stop sending data" << std::endl;
108 | return -1;
109 | }
110 | return 0;
111 | }
112 |
113 | int VideoWriter::Write(const cv::Mat& frame, double timestamp) {
114 | return PushData2Pipeline(frame, timestamp);
115 | }
116 |
--------------------------------------------------------------------------------
/VideoWriter.h:
--------------------------------------------------------------------------------
1 | #ifndef __VIDEO_WRITER_H__
2 | #define __VIDEO_WRITER_H__
3 |
4 |
5 | #include
6 | #include "opencv2/opencv.hpp"
7 | #include
8 |
9 | class VideoWriter {
10 | public:
11 | ~VideoWriter();
12 | /**
13 | * @brief 打开视频, url为视频的文件路径或者网络地址
14 | */
15 | int Open(const std::string url);
16 |
17 | /**
18 | * @brief 设置视频帧率
19 | * @param fps = framerate.first/framerate.second
20 | */
21 | void SetFramerate(std::pair framerate) {
22 | framerate_ = framerate;
23 | }
24 |
25 | /**
26 | * @brief 设置视频分辨率
27 | */
28 | void SetSize(int width, int height) {
29 | width_ = width;
30 | height_ = height;
31 | }
32 |
33 | /**
34 | * @brief 设置视频码率
35 | * @param bitrate 单位bit/sec
36 | */
37 | void SetBitrate(int bitrate) {
38 | bitrate_ = bitrate;
39 | }
40 |
41 | /**
42 | * @brief 写入视频帧
43 | * @param timestamp 单位秒
44 | */
45 | int Write(const cv::Mat& frame, double timestamp) ;
46 |
47 | private:
48 | int PushData2Pipeline(const cv::Mat& frame, double timestamp);
49 | GstElement *pipeline_;
50 | GstElement *appSrc_;
51 | GstElement *queue_;
52 | GstElement *videoConvert_;
53 | GstElement *encoder_;
54 | GstElement *capsFilter_;
55 | GstElement *mux_;
56 | GstElement *sink_;
57 | int width_= 0;
58 | int height_ = 0;
59 | int bitrate_ = 0;
60 | std::pair framerate_{30, 1};
61 | };
62 |
63 | #endif
--------------------------------------------------------------------------------
/WriterDemo.cc:
--------------------------------------------------------------------------------
1 | #include "VideoReader.h"
2 | #include "VideoWriter.h"
3 | #include
4 | #include
5 |
6 | void TestVideoReadWrite(std::string url, std::string outUrl, int count) {
7 | std::cout << "video:" << url << std::endl;
8 |
9 | int width = 1920, height = 1080;
10 | VideoReader reader;
11 | reader.InputOriginSize(width, height);
12 | auto ret = reader.Open(url);
13 | if (ret < 0) return;
14 |
15 | VideoWriter writer;
16 | writer.SetSize(width, height);
17 | writer.SetFramerate(reader.Framerate());
18 | ret = writer.Open(outUrl);
19 | if (ret < 0) return;
20 |
21 | cv::Mat frame;
22 | int seq = 0;
23 | double timestamp = .0;
24 | while (seq++ < count) {
25 | auto ret = reader.Read(frame, timestamp);
26 | if (ret < 0) break;
27 | // std::string filename = "./bin/" + std::to_string(seq) + ".jpg";
28 | // cv::imwrite(filename, frame);
29 | writer.Write(frame, timestamp);
30 | }
31 |
32 | std::cout << "video read write test exit" << std::endl;
33 | }
34 |
35 | int main(int argc, char* argv[]) {
36 | gst_init(&argc, &argv);
37 |
38 | std::string inputUrl(argv[1]);
39 | std::string outputUrl(argv[2]);
40 |
41 | TestVideoReadWrite(inputUrl, outputUrl, 100);
42 |
43 | return 0;
44 | }
--------------------------------------------------------------------------------
/image/decode_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jjungle/VideoReaderWriteOfRB5/5e931291942d4f0f508779e5d265d65d05be51de/image/decode_flow.png
--------------------------------------------------------------------------------
/image/encode_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jjungle/VideoReaderWriteOfRB5/5e931291942d4f0f508779e5d265d65d05be51de/image/encode_flow.png
--------------------------------------------------------------------------------
/input/input.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jjungle/VideoReaderWriteOfRB5/5e931291942d4f0f508779e5d265d65d05be51de/input/input.mp4
--------------------------------------------------------------------------------