Skip to content

Commit f80984a

Browse files
committed
Merge branch 'master' of https://github.com/Raveler/ffmpeg-cpp
2 parents 76f5796 + 9e56139 commit f80984a

File tree

14 files changed

+204
-60
lines changed

14 files changed

+204
-60
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
/source/ffmpeg-cpp/encode_audio/obj
1919
/source/ffmpeg-cpp/encode_video/obj
2020
/source/ffmpeg-cpp/decode_video/obj
21-
/source/ffmpeg-cpp/filter_video/obj
21+
/source/ffmpeg-cpp/filtering_video/obj
2222
/include
2323

2424
/source/ffmpeg-cpp/remuxing/obj

README.md

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,74 @@
11
# ffmpeg-cpp
2-
A clean C++ wrapper around the ffmpeg libraries
2+
A clean C++ wrapper around the ffmpeg libraries. Makes the most commonly used functionality of FFmpeg easily available for any C++ projects with an easy-to-use interface. The full power of FFmpeg compacted in 10 lines of C++ code: if this sounds useful to you, read on!
3+
4+
# Installation
5+
6+
## Windows
7+
8+
1. Clone the repository
9+
2. Download a build of FFmpeg from https://ffmpeg.zeranoe.com/builds/. The project was last tested with **4.1**. You will need both the dev version (for .h and .lib files) and the shared version (for .dll). Extract all of them into the ffmpeg directory in the repository. there are more instructions on how to extract them properly in the ffmpeg/readme.txt file.
10+
3. Open the Visual Studio solution in the source directory.
11+
4. Build everything.
12+
13+
This will build a .lib file that you can use in your own C++ projects. It will also generate an include-folder in the repo root that you can add to your include filders in your own project.
14+
15+
## Linux
16+
17+
Currently, only a Windows environment with Visual Studio is supported. This is simply because I do not have experience with cmake and Linux-projects, since the project itself is fully platform-independent. If anyone is willing to help me out with configuring cmake, please get in touch!
18+
19+
# Usage
20+
21+
There are multiple demo projects included in the solution. Check out the demo-project for a thorough exploration of the features (demuxing, decoding, filtering, encoding, muxing) or one of the other examples for a simpler example to follow.
22+
23+
To give you an idea, this code will load a video stream from a container, filter it, and write it back out to another container:
24+
25+
```C++
26+
// Create a muxer that will output the video as MP4.
27+
Muxer* muxer = new Muxer("filtered_video.mp4");
28+
29+
// Create a MPEG2 codec that will encode the raw data.
30+
VideoCodec* codec = new VideoCodec(AV_CODEC_ID_MPEG2VIDEO);
31+
32+
// Create an encoder that will encode the raw audio data using the codec specified above.
33+
// Tie it to the muxer so it will be written to file.
34+
VideoEncoder* encoder = new VideoEncoder(codec, muxer);
35+
36+
// Create a video filter and do some funny stuff with the video data.
37+
VideoFilter* filter = new VideoFilter("scale=640:150,transpose=cclock,vignette", encoder);
38+
39+
// Load a container. Pick the best video stream container in the container
40+
// And send it to the filter.
41+
Demuxer* demuxer = new Demuxer("big_buck_bunny.mp4");
42+
demuxer->DecodeBestVideoStream(filter);
43+
44+
// Prepare the output pipeline.
45+
// This will decode a small amount of frames so the pipeline can configure itself.
46+
demuxer->PreparePipeline();
47+
48+
// Push all the remaining frames through.
49+
while (!demuxer->IsDone())
50+
{
51+
demuxer->Step();
52+
}
53+
54+
// Save everything to disk by closing the muxer.
55+
muxer->Close();
56+
57+
```
58+
59+
# Why?
60+
61+
I developed this project to be able to to integrate FFmpeg into our program without having to call the executable to do an operation. This is important because starting up an external executable tends to be blocked by antivirus software and can cause issues with users. It has been tested for the most common functionality, and some of the examples from https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples are mirrored in the project as well.
62+
63+
# Roadmap
64+
65+
- Add Linux/Mac build support
66+
- Audio filtering
67+
- Adding proper unit tests
68+
- Testing with more codecs, containers
69+
70+
# License
71+
72+
This library is licensed under LGPL (https://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License).
73+
74+
Please note though that FFmpeg, which you will need to build this library, is not. Depending on how you build it, it is either LGPL or GPL. So once you use the LGPL-version of FFmpeg in your project, this library will be GPL too.

ffmpeg/readme.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ In this folder, you need to put your ffmpeg binaries, library files and includes
33
The structure should be as follows:
44
/include contains all header files for the ffmpeg project
55
/lib contains all .lib files
6+
/bin contains all .dll files
67

78
For Windows, you can get the necessary files from:
89
https://www.ffmpeg.org/download.html

source/ffmpeg-cpp/demo/demo.vcxproj.user

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
4-
<LocalDebuggerCommandArguments>"D:\WonderMedia\ffmpeg\samples\carphone_qcif.y4m" "D:\WonderMedia\ffmpeg\samples\DesiJourney.wav" "D:\WonderMedia\ffmpeg\samples\out.mp4"</LocalDebuggerCommandArguments>
5-
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
6-
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
7-
</PropertyGroup>
8-
<PropertyGroup>
9-
<ShowAllFiles>false</ShowAllFiles>
10-
</PropertyGroup>
113
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
124
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
135
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
@@ -16,6 +8,10 @@
168
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
179
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
1810
</PropertyGroup>
11+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
12+
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
13+
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
14+
</PropertyGroup>
1915
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
2016
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
2117
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>

source/ffmpeg-cpp/encode_video/encode_video.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ int main()
1414
// Create a muxer that will output the video as MKV.
1515
Muxer* muxer = new Muxer("output.mpg");
1616

17-
// Create a MP3 codec that will encode the raw data.
17+
// Create a MPEG2 codec that will encode the raw data.
1818
VideoCodec* codec = new VideoCodec("mpeg2video");
19-
//codec->SetOption("preset", "default");
20-
//VideoCodec* codec = new H264NVEncCodec();
19+
20+
// Set the global quality of the video encoding. This maps to the command line
21+
// parameter -qscale and must be within range [0,31].
22+
codec->SetQualityScale(0);
2123

2224
// Create an encoder that will encode the raw audio data as MP3.
2325
// Tie it to the muxer so it will be written to the file.
@@ -41,16 +43,12 @@ int main()
4143
// Save everything to disk by closing the muxer.
4244
muxer->Close();
4345
}
44-
catch (const char* bla)
45-
{
46-
47-
}
48-
/*catch (FFmpegException e)
46+
catch (FFmpegException e)
4947
{
5048
cerr << "Exception caught!" << endl;
5149
cerr << e.what() << endl;
5250
throw e;
53-
}*/
51+
}
5452

5553
cout << "Encoding complete!" << endl;
5654
cout << "Press any key to continue..." << endl;

source/ffmpeg-cpp/ffmpeg-cpp/Codecs/VideoCodec.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ namespace ffmpegcpp
1919
{
2020
}
2121

22+
void VideoCodec::SetQualityScale(int qscale)
23+
{
24+
25+
codecContext->flags |= AV_CODEC_FLAG_QSCALE;
26+
codecContext->global_quality = FF_QP2LAMBDA * 0;
27+
}
28+
2229
bool VideoCodec::IsPixelFormatSupported(AVPixelFormat format)
2330
{
2431
if (format == AV_PIX_FMT_NONE) return true; // let the codec deal with this
@@ -77,4 +84,37 @@ namespace ffmpegcpp
7784
if (*p == AV_PIX_FMT_NONE) throw FFmpegException("Codec " + string(codecContext->codec->name) + " does not have a default pixel format, you have to specify one");
7885
return *p;
7986
}
87+
88+
AVRational VideoCodec::GetClosestSupportedFrameRate(AVRational originalFrameRate)
89+
{
90+
if (!codecContext->codec->supported_framerates)
91+
{
92+
// make up a frame rate - there is no supported frame rate
93+
return originalFrameRate;
94+
};
95+
96+
const AVRational *p = codecContext->codec->supported_framerates;
97+
AVRational bestFrameRate;
98+
bestFrameRate.num = 0;
99+
bestFrameRate.den = 1;
100+
double bestDiff = std::numeric_limits<double>::max();
101+
double fVal = av_q2d(originalFrameRate);
102+
while (p->num)
103+
{
104+
double pVal = av_q2d(*p);
105+
double diff = abs(pVal - fVal);
106+
if (diff < bestDiff)
107+
{
108+
bestDiff = diff;
109+
bestFrameRate.num = p->num;
110+
bestFrameRate.den = p->den;
111+
}
112+
p++;
113+
}
114+
115+
// There were no valid frame rates in the list... this should never happen unless ffmpeg screws up.
116+
if (bestFrameRate.num == 0) return originalFrameRate;
117+
118+
return bestFrameRate;
119+
}
80120
}

source/ffmpeg-cpp/ffmpeg-cpp/Codecs/VideoCodec.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ namespace ffmpegcpp
1616

1717
OpenCodec* Open(int width, int height, AVRational* frameRate, AVPixelFormat format);
1818

19+
// This maps to the qscale parameter so should be in the range [0,31].
20+
void SetQualityScale(int qscale);
21+
1922
bool IsPixelFormatSupported(AVPixelFormat format);
2023
bool IsFrameRateSupported(AVRational* frameRate);
2124

2225
AVPixelFormat GetDefaultPixelFormat();
26+
AVRational GetClosestSupportedFrameRate(AVRational frameRate);
2327

2428
};
2529

source/ffmpeg-cpp/ffmpeg-cpp/Frame Sinks/VideoEncoder.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,18 @@ namespace ffmpegcpp
7171
// configure the parameters for the codec based on the frame and our preferences
7272
int width = frame->width;
7373
int height = frame->height;
74+
75+
// the format is either the provided format, or the default format if it is not supported
7476
AVPixelFormat format = finalPixelFormat;
77+
if (!closedCodec->IsPixelFormatSupported(format)) format = closedCodec->GetDefaultPixelFormat();
7578
if (format == AV_PIX_FMT_NONE) format = closedCodec->GetDefaultPixelFormat();
79+
80+
// the frame rate is either the input frame rate, OR the default frame rate if the input frame rate
81+
// is not supported, OR the explicitly chosen framerate.
7682
AVRational frameRate;
7783
frameRate.num = timeBase->den;
7884
frameRate.den = timeBase->num;
85+
if (!closedCodec->IsFrameRateSupported(&frameRate)) frameRate = closedCodec->GetClosestSupportedFrameRate(frameRate);
7986
if (finalFrameRateSet) frameRate = finalFrameRate;
8087

8188
// open the codec

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/AudioOutputStream.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ namespace ffmpegcpp
3737
}
3838

3939
codecTimeBase = openCodec->GetContext()->time_base;
40+
41+
// Copy side_data from the codec context to the stream... this is
42+
// necessary for certain codecs such as mpeg2video!
43+
if (openCodec->GetContext()->nb_coded_side_data)
44+
{
45+
int i;
46+
47+
for (i = 0; i < openCodec->GetContext()->nb_coded_side_data; i++)
48+
{
49+
const AVPacketSideData *sd_src = &openCodec->GetContext()->coded_side_data[i];
50+
uint8_t *dst_data;
51+
52+
dst_data = av_stream_new_side_data(stream, sd_src->type, sd_src->size);
53+
if (!dst_data)
54+
{
55+
throw FFmpegException("Failed to allocate memory for new side_data");
56+
}
57+
memcpy(dst_data, sd_src->data, sd_src->size);
58+
}
59+
}
4060
}
4161

4262
void AudioOutputStream::WritePacket(AVPacket *pkt, OpenCodec* openCodec)

source/ffmpeg-cpp/ffmpeg-cpp/Muxing/Muxer.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ namespace ffmpegcpp
1717
avformat_alloc_output_context2(&containerContext, NULL, NULL, fileName);
1818
if (!containerContext)
1919
{
20-
printf("WARNING: Could not deduce output format from file extension: using MPEG. as default\n");
21-
avformat_alloc_output_context2(&containerContext, NULL, "mpeg", fileName);
20+
printf("WARNING: Could not deduce output format from file extension: using MP4. as default\n");
21+
avformat_alloc_output_context2(&containerContext, NULL, "mp4", fileName);
2222
}
2323
if (!containerContext)
2424
{
@@ -38,6 +38,12 @@ namespace ffmpegcpp
3838
{
3939
if (containerContext != nullptr)
4040
{
41+
// if some of the output streams weren't primed, we cannot finish this process
42+
if (!IsPrimed())
43+
{
44+
throw FFmpegException("You cannot close a muxer when one of the streams wasn't primed. You need to make sure all streams are primed before closing the muxer.");
45+
}
46+
4147
/* Write the trailer, if any. The trailer must be written before you
4248
* close the CodecContexts open when you wrote the header; otherwise
4349
* av_write_trailer() may try to use memory that was freed on

0 commit comments

Comments
 (0)