Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
F
ffmpeg_kit_flutter
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
openSourceLibrary
ffmpeg_kit_flutter
Commits
f23762b9
提交
f23762b9
authored
4月 17, 2025
作者:
祁增奎
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Android替换
上级
5e4362bc
隐藏空白字符变更
内嵌
并排
正在显示
29 个修改的文件
包含
6090 行增加
和
425 行删除
+6090
-425
README.md
android/README.md
+3
-3
Doxyfile
android/ffmpeg-kit-android-lib/Doxyfile
+1
-1
consumer-rules.pro
android/ffmpeg-kit-android-lib/consumer-rules.pro
+1
-1
ffmpegkit.c
android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.c
+31
-22
ffmpegkit.h
android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.h
+1
-1
fftools_cmdutils.c
...id/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.c
+24
-22
fftools_cmdutils.h
...id/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.h
+19
-6
fftools_ffmpeg.c
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.c
+0
-0
fftools_ffmpeg.h
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.h
+268
-102
fftools_ffmpeg_demux.c
...fmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_demux.c
+1148
-0
fftools_ffmpeg_filter.c
...mpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_filter.c
+201
-88
fftools_ffmpeg_hw.c
...d/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_hw.c
+17
-10
fftools_ffmpeg_mux.c
.../ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.c
+566
-116
fftools_ffmpeg_mux.h
.../ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.h
+165
-0
fftools_ffmpeg_mux_init.c
...eg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux_init.c
+2414
-0
fftools_ffmpeg_opt.c
.../ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_opt.c
+0
-0
fftools_ffprobe.c
...oid/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffprobe.c
+80
-39
fftools_objpool.c
...oid/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.c
+145
-0
fftools_objpool.h
...oid/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.h
+50
-0
fftools_opt_common.c
.../ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.c
+9
-2
fftools_opt_common.h
.../ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.h
+1
-1
fftools_sync_queue.c
.../ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.c
+462
-0
fftools_sync_queue.h
.../ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.h
+122
-0
fftools_thread_queue.c
...fmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.c
+259
-0
fftools_thread_queue.h
...fmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.h
+94
-0
FFmpegKitConfig.java
...rc/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java
+2
-4
NativeLoader.java
...b/src/main/java/com/arthenica/ffmpegkit/NativeLoader.java
+2
-2
Statistics.java
...lib/src/main/java/com/arthenica/ffmpegkit/Statistics.java
+4
-4
Android.mk
android/jni/Android.mk
+1
-1
没有找到文件。
android/README.md
浏览文件 @
f23762b9
...
@@ -22,14 +22,14 @@ external libraries enabled.
...
@@ -22,14 +22,14 @@ external libraries enabled.
##### 2.1.1 Android Tools
##### 2.1.1 Android Tools
-
Android SDK Build Tools
-
Android SDK Build Tools
-
Android NDK r22b or later with LLDB and CMake (See
[
#292
](
https://github.com/arthenica/ffmpeg-kit/issues/292
)
if you want to use NDK r23b)
-
Android NDK r22b or later with LLDB and CMake (See
[
#292
](
https://github.com/arthenica/ffmpeg-kit/issues/292
)
if you want to use NDK r23b
or later
)
##### 2.1.2 Packages
##### 2.1.2 Packages
Use your package manager (apt, yum, dnf, brew, etc.) to install the following packages.
Use your package manager (apt, yum, dnf, brew, etc.) to install the following packages.
```
```
autoconf automake libtool pkg-config curl
cmake gcc gperf texinfo yasm nasm bison autogen git wget autopoint meson ninja
autoconf automake libtool pkg-config curl
git doxygen nasm cmake gcc gperf texinfo yasm bison autogen wget autopoint meson ninja ragel groff gtk-doc-tools libtasn1
```
```
##### 2.1.3 Environment Variables
##### 2.1.3 Environment Variables
...
@@ -77,7 +77,7 @@ All libraries created by `android.sh` can be found under the `prebuilt` director
...
@@ -77,7 +77,7 @@ All libraries created by `android.sh` can be found under the `prebuilt` director
}
}
dependencies {
dependencies {
implementation 'com.arthenica:ffmpeg-kit-full:
5.1
'
implementation 'com.arthenica:ffmpeg-kit-full:
6.0-2
'
}
}
```
```
...
...
android/ffmpeg-kit-android-lib/Doxyfile
浏览文件 @
f23762b9
...
@@ -38,7 +38,7 @@ PROJECT_NAME = "FFmpegKit Android API"
...
@@ -38,7 +38,7 @@ PROJECT_NAME = "FFmpegKit Android API"
# could be handy for archiving the generated documentation or if some version
# could be handy for archiving the generated documentation or if some version
# control system is used.
# control system is used.
PROJECT_NUMBER =
5.1
PROJECT_NUMBER =
6.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# for a project that appears at the top of each page and should give viewer a
...
...
android/ffmpeg-kit-android-lib/consumer-rules.pro
浏览文件 @
f23762b9
-
keep
class
com
.
arthenica
.
ffmpegkit
.
FFmpegKitConfig
{
-
keep
class
com
.
arthenica
.
ffmpegkit
.
FFmpegKitConfig
{
native
<
methods
>
;
native
<
methods
>
;
void
log
(
long
,
int
,
byte
[]);
void
log
(
long
,
int
,
byte
[]);
void
statistics
(
long
,
int
,
float
,
float
,
long
,
int
,
double
,
double
);
void
statistics
(
long
,
int
,
float
,
float
,
long
,
double
,
double
,
double
);
int
safOpen
(
int
);
int
safOpen
(
int
);
int
safClose
(
int
);
int
safClose
(
int
);
}
}
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.c
浏览文件 @
f23762b9
...
@@ -45,7 +45,7 @@ struct CallbackData {
...
@@ -45,7 +45,7 @@ struct CallbackData {
float
statisticsFps
;
// statistics fps
float
statisticsFps
;
// statistics fps
float
statisticsQuality
;
// statistics quality
float
statisticsQuality
;
// statistics quality
int64_t
statisticsSize
;
// statistics size
int64_t
statisticsSize
;
// statistics size
int
statisticsTime
;
// statistics time
double
statisticsTime
;
// statistics time
double
statisticsBitrate
;
// statistics bitrate
double
statisticsBitrate
;
// statistics bitrate
double
statisticsSpeed
;
// statistics speed
double
statisticsSpeed
;
// statistics speed
...
@@ -312,7 +312,7 @@ void logCallbackDataAdd(int level, AVBPrint *data) {
...
@@ -312,7 +312,7 @@ void logCallbackDataAdd(int level, AVBPrint *data) {
/**
/**
* Adds statistics data to the end of callback data list.
* Adds statistics data to the end of callback data list.
*/
*/
void
statisticsCallbackDataAdd
(
int
frameNumber
,
float
fps
,
float
quality
,
int64_t
size
,
int
time
,
double
bitrate
,
double
speed
)
{
void
statisticsCallbackDataAdd
(
int
frameNumber
,
float
fps
,
float
quality
,
int64_t
size
,
double
time
,
double
bitrate
,
double
speed
)
{
// CREATE DATA STRUCT FIRST
// CREATE DATA STRUCT FIRST
struct
CallbackData
*
newData
=
(
struct
CallbackData
*
)
av_malloc
(
sizeof
(
struct
CallbackData
));
struct
CallbackData
*
newData
=
(
struct
CallbackData
*
)
av_malloc
(
sizeof
(
struct
CallbackData
));
...
@@ -491,7 +491,7 @@ void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, v
...
@@ -491,7 +491,7 @@ void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, v
* @param bitrate output bit rate in kbits/s
* @param bitrate output bit rate in kbits/s
* @param speed processing speed = processed duration / operation duration
* @param speed processing speed = processed duration / operation duration
*/
*/
void
ffmpegkit_statistics_callback_function
(
int
frameNumber
,
float
fps
,
float
quality
,
int64_t
size
,
int
time
,
double
bitrate
,
double
speed
)
{
void
ffmpegkit_statistics_callback_function
(
int
frameNumber
,
float
fps
,
float
quality
,
int64_t
size
,
double
time
,
double
bitrate
,
double
speed
)
{
statisticsCallbackDataAdd
(
frameNumber
,
fps
,
quality
,
size
,
time
,
bitrate
,
speed
);
statisticsCallbackDataAdd
(
frameNumber
,
fps
,
quality
,
size
,
time
,
bitrate
,
speed
);
}
}
...
@@ -581,6 +581,30 @@ int saf_close(int fd) {
...
@@ -581,6 +581,30 @@ int saf_close(int fd) {
return
(
*
env
)
->
CallStaticIntMethod
(
env
,
configClass
,
safCloseMethod
,
fd
);
return
(
*
env
)
->
CallStaticIntMethod
(
env
,
configClass
,
safCloseMethod
,
fd
);
}
}
/**
* Used by JNI methods to enable redirection.
*/
static
void
enableNativeRedirection
()
{
mutexLock
();
if
(
redirectionEnabled
!=
0
)
{
mutexUnlock
();
return
;
}
redirectionEnabled
=
1
;
mutexUnlock
();
int
rc
=
pthread_create
(
&
callbackThread
,
0
,
callbackThreadFunction
,
0
);
if
(
rc
!=
0
)
{
LOGE
(
"Failed to create callback thread (rc=%d).
\n
"
,
rc
);
return
;
}
av_log_set_callback
(
ffmpegkit_log_callback_function
);
set_report_callback
(
ffmpegkit_statistics_callback_function
);
}
/**
/**
* Called when 'ffmpegkit' native library is loaded.
* Called when 'ffmpegkit' native library is loaded.
*
*
...
@@ -620,7 +644,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
...
@@ -620,7 +644,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return
JNI_FALSE
;
return
JNI_FALSE
;
}
}
statisticsMethod
=
(
*
env
)
->
GetStaticMethodID
(
env
,
localConfigClass
,
"statistics"
,
"(JIFFJ
I
DD)V"
);
statisticsMethod
=
(
*
env
)
->
GetStaticMethodID
(
env
,
localConfigClass
,
"statistics"
,
"(JIFFJ
D
DD)V"
);
if
(
statisticsMethod
==
NULL
)
{
if
(
statisticsMethod
==
NULL
)
{
LOGE
(
"OnLoad thread failed to GetStaticMethodID for %s.
\n
"
,
"statistics"
);
LOGE
(
"OnLoad thread failed to GetStaticMethodID for %s.
\n
"
,
"statistics"
);
return
JNI_FALSE
;
return
JNI_FALSE
;
...
@@ -665,6 +689,8 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
...
@@ -665,6 +689,8 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
av_set_saf_open
(
saf_open
);
av_set_saf_open
(
saf_open
);
av_set_saf_close
(
saf_close
);
av_set_saf_close
(
saf_close
);
enableNativeRedirection
();
return
JNI_VERSION_1_6
;
return
JNI_VERSION_1_6
;
}
}
...
@@ -696,24 +722,7 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLog
...
@@ -696,24 +722,7 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLog
* @param object reference to the class on which this method is invoked
* @param object reference to the class on which this method is invoked
*/
*/
JNIEXPORT
void
JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection
(
JNIEnv
*
env
,
jclass
object
)
{
JNIEXPORT
void
JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection
(
JNIEnv
*
env
,
jclass
object
)
{
mutexLock
();
enableNativeRedirection
();
if
(
redirectionEnabled
!=
0
)
{
mutexUnlock
();
return
;
}
redirectionEnabled
=
1
;
mutexUnlock
();
int
rc
=
pthread_create
(
&
callbackThread
,
0
,
callbackThreadFunction
,
0
);
if
(
rc
!=
0
)
{
LOGE
(
"Failed to create callback thread (rc=%d).
\n
"
,
rc
);
return
;
}
av_log_set_callback
(
ffmpegkit_log_callback_function
);
set_report_callback
(
ffmpegkit_statistics_callback_function
);
}
}
/**
/**
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.h
浏览文件 @
f23762b9
...
@@ -27,7 +27,7 @@
...
@@ -27,7 +27,7 @@
#include "libavutil/ffversion.h"
#include "libavutil/ffversion.h"
/** Library version string */
/** Library version string */
#define FFMPEG_KIT_VERSION "
5.1
"
#define FFMPEG_KIT_VERSION "
6.0
"
/** Defines tag used for Android logging. */
/** Defines tag used for Android logging. */
#define LIB_NAME "ffmpeg-kit"
#define LIB_NAME "ffmpeg-kit"
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.c
浏览文件 @
f23762b9
/*
/*
* Various utilities for command line tools
* Various utilities for command line tools
* Copyright (c) 2000-2003 Fabrice Bellard
* Copyright (c) 2000-2003 Fabrice Bellard
* copyright (c) 2018 Taner Sener ( tanersener gmail com )
* Copyright (c) 2018-2022 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
*
*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
*
*
...
@@ -25,6 +26,12 @@
...
@@ -25,6 +26,12 @@
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
*
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
*
*
* 09.2022
* 09.2022
...
@@ -129,11 +136,18 @@ void register_exit(void (*cb)(int ret))
...
@@ -129,11 +136,18 @@ void register_exit(void (*cb)(int ret))
program_exit
=
cb
;
program_exit
=
cb
;
}
}
void
report_and_exit
(
int
ret
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"%s
\n
"
,
av_err2str
(
ret
));
exit_program
(
AVUNERROR
(
ret
));
}
void
exit_program
(
int
ret
)
void
exit_program
(
int
ret
)
{
{
if
(
program_exit
)
if
(
program_exit
)
program_exit
(
ret
);
program_exit
(
ret
);
// FFmpegKit
// exit disabled and replaced with longjmp, exit value stored in longjmp_value
// exit disabled and replaced with longjmp, exit value stored in longjmp_value
// exit(ret);
// exit(ret);
longjmp_value
=
ret
;
longjmp_value
=
ret
;
...
@@ -696,7 +710,7 @@ static void init_parse_context(OptionParseContext *octx,
...
@@ -696,7 +710,7 @@ static void init_parse_context(OptionParseContext *octx,
octx
->
nb_groups
=
nb_groups
;
octx
->
nb_groups
=
nb_groups
;
octx
->
groups
=
av_calloc
(
octx
->
nb_groups
,
sizeof
(
*
octx
->
groups
));
octx
->
groups
=
av_calloc
(
octx
->
nb_groups
,
sizeof
(
*
octx
->
groups
));
if
(
!
octx
->
groups
)
if
(
!
octx
->
groups
)
exit_program
(
1
);
report_and_exit
(
AVERROR
(
ENOMEM
)
);
for
(
i
=
0
;
i
<
octx
->
nb_groups
;
i
++
)
for
(
i
=
0
;
i
<
octx
->
nb_groups
;
i
++
)
octx
->
groups
[
i
].
group_def
=
&
groups
[
i
];
octx
->
groups
[
i
].
group_def
=
&
groups
[
i
];
...
@@ -843,12 +857,7 @@ do { \
...
@@ -843,12 +857,7 @@ do { \
void
print_error
(
const
char
*
filename
,
int
err
)
void
print_error
(
const
char
*
filename
,
int
err
)
{
{
char
errbuf
[
128
];
av_log
(
NULL
,
AV_LOG_ERROR
,
"%s: %s
\n
"
,
filename
,
av_err2str
(
err
));
const
char
*
errbuf_ptr
=
errbuf
;
if
(
av_strerror
(
err
,
errbuf
,
sizeof
(
errbuf
))
<
0
)
errbuf_ptr
=
strerror
(
AVUNERROR
(
err
));
av_log
(
NULL
,
AV_LOG_ERROR
,
"%s: %s
\n
"
,
filename
,
errbuf_ptr
);
}
}
int
read_yesno
(
void
)
int
read_yesno
(
void
)
...
@@ -971,7 +980,7 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
...
@@ -971,7 +980,7 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
break
;
break
;
}
}
while
((
t
=
av_dict_
get
(
opts
,
""
,
t
,
AV_DICT_IGNORE_SUFFIX
)))
{
while
((
t
=
av_dict_
iterate
(
opts
,
t
)))
{
const
AVClass
*
priv_class
;
const
AVClass
*
priv_class
;
char
*
p
=
strchr
(
t
->
key
,
':'
);
char
*
p
=
strchr
(
t
->
key
,
':'
);
...
@@ -1009,11 +1018,8 @@ AVDictionary **setup_find_stream_info_opts(AVFormatContext *s,
...
@@ -1009,11 +1018,8 @@ AVDictionary **setup_find_stream_info_opts(AVFormatContext *s,
if
(
!
s
->
nb_streams
)
if
(
!
s
->
nb_streams
)
return
NULL
;
return
NULL
;
opts
=
av_calloc
(
s
->
nb_streams
,
sizeof
(
*
opts
));
opts
=
av_calloc
(
s
->
nb_streams
,
sizeof
(
*
opts
));
if
(
!
opts
)
{
if
(
!
opts
)
av_log
(
NULL
,
AV_LOG_ERROR
,
report_and_exit
(
AVERROR
(
ENOMEM
));
"Could not alloc memory for stream options.
\n
"
);
exit_program
(
1
);
}
for
(
i
=
0
;
i
<
s
->
nb_streams
;
i
++
)
for
(
i
=
0
;
i
<
s
->
nb_streams
;
i
++
)
opts
[
i
]
=
filter_codec_opts
(
codec_opts
,
s
->
streams
[
i
]
->
codecpar
->
codec_id
,
opts
[
i
]
=
filter_codec_opts
(
codec_opts
,
s
->
streams
[
i
]
->
codecpar
->
codec_id
,
s
,
s
->
streams
[
i
],
NULL
);
s
,
s
->
streams
[
i
],
NULL
);
...
@@ -1028,10 +1034,8 @@ void *grow_array(void *array, int elem_size, int *size, int new_size)
...
@@ -1028,10 +1034,8 @@ void *grow_array(void *array, int elem_size, int *size, int new_size)
}
}
if
(
*
size
<
new_size
)
{
if
(
*
size
<
new_size
)
{
uint8_t
*
tmp
=
av_realloc_array
(
array
,
new_size
,
elem_size
);
uint8_t
*
tmp
=
av_realloc_array
(
array
,
new_size
,
elem_size
);
if
(
!
tmp
)
{
if
(
!
tmp
)
av_log
(
NULL
,
AV_LOG_ERROR
,
"Could not alloc buffer.
\n
"
);
report_and_exit
(
AVERROR
(
ENOMEM
));
exit_program
(
1
);
}
memset
(
tmp
+
*
size
*
elem_size
,
0
,
(
new_size
-*
size
)
*
elem_size
);
memset
(
tmp
+
*
size
*
elem_size
,
0
,
(
new_size
-*
size
)
*
elem_size
);
*
size
=
new_size
;
*
size
=
new_size
;
return
tmp
;
return
tmp
;
...
@@ -1044,10 +1048,8 @@ void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems)
...
@@ -1044,10 +1048,8 @@ void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems)
void
*
new_elem
;
void
*
new_elem
;
if
(
!
(
new_elem
=
av_mallocz
(
elem_size
))
||
if
(
!
(
new_elem
=
av_mallocz
(
elem_size
))
||
av_dynarray_add_nofree
(
ptr
,
nb_elems
,
new_elem
)
<
0
)
{
av_dynarray_add_nofree
(
ptr
,
nb_elems
,
new_elem
)
<
0
)
av_log
(
NULL
,
AV_LOG_ERROR
,
"Could not alloc buffer.
\n
"
);
report_and_exit
(
AVERROR
(
ENOMEM
));
exit_program
(
1
);
}
return
new_elem
;
return
new_elem
;
}
}
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.h
浏览文件 @
f23762b9
/*
/*
* Various utilities for command line tools
* Various utilities for command line tools
* copyright (c) 2003 Fabrice Bellard
* copyright (c) 2003 Fabrice Bellard
* copyright (c) 2018 Taner Sener ( tanersener gmail com )
* copyright (c) 2018-2022 Taner Sener
* copyright (c) 2023 ARTHENICA LTD
*
*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
*
*
...
@@ -25,6 +26,12 @@
...
@@ -25,6 +26,12 @@
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
*
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
*
*
* 09.2022
* 09.2022
...
@@ -95,6 +102,17 @@ extern __thread int find_stream_info;
...
@@ -95,6 +102,17 @@ extern __thread int find_stream_info;
*/
*/
void
register_exit
(
void
(
*
cb
)(
int
ret
));
void
register_exit
(
void
(
*
cb
)(
int
ret
));
/**
* Reports an error corresponding to the provided
* AVERROR code and calls exit_program() with the
* corresponding POSIX error code.
* @note ret must be an AVERROR-value of a POSIX error code
* (i.e. AVERROR(EFOO) and not AVERROR_FOO).
* library functions can return both, so call this only
* with AVERROR(EFOO) of your own.
*/
void
report_and_exit
(
int
ret
)
av_noreturn
;
/**
/**
* Wraps exit with a program-specific cleanup routine.
* Wraps exit with a program-specific cleanup routine.
*/
*/
...
@@ -232,11 +250,6 @@ void show_help_children(const AVClass *clazz, int flags);
...
@@ -232,11 +250,6 @@ void show_help_children(const AVClass *clazz, int flags);
void
show_help_default_ffmpeg
(
const
char
*
opt
,
const
char
*
arg
);
void
show_help_default_ffmpeg
(
const
char
*
opt
,
const
char
*
arg
);
void
show_help_default_ffprobe
(
const
char
*
opt
,
const
char
*
arg
);
void
show_help_default_ffprobe
(
const
char
*
opt
,
const
char
*
arg
);
/**
* Generic -h handler common to all fftools.
*/
int
show_help
(
void
*
optctx
,
const
char
*
opt
,
const
char
*
arg
);
/**
/**
* Parse the command line arguments.
* Parse the command line arguments.
*
*
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.c
浏览文件 @
f23762b9
This source diff could not be displayed because it is too large. You can
view the blob
instead.
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.h
浏览文件 @
f23762b9
/*
/*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
* Copyright (c) 2018 Taner Sener ( tanersener gmail com )
* Copyright (c) 2018-2022 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
*
*
* FFmpeg is free software; you can redistribute it and/or
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* modify it under the terms of the GNU Lesser General Public
...
@@ -22,6 +23,16 @@
...
@@ -22,6 +23,16 @@
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT, SPECIFIER_OPT_FMT declarations migrated to
* ffmpeg_mux.h
* - "class" member field renamed as clazz
* - time field in set_report_callback updated as double
*
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
*
*
* 09.2022
* 09.2022
...
@@ -59,11 +70,13 @@
...
@@ -59,11 +70,13 @@
#include "config.h"
#include "config.h"
#include <stdatomic.h>
#include <stdint.h>
#include <stdint.h>
#include <stdio.h>
#include <stdio.h>
#include <signal.h>
#include <signal.h>
#include "fftools_cmdutils.h"
#include "fftools_cmdutils.h"
#include "fftools_sync_queue.h"
#include "libavformat/avformat.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavformat/avio.h"
...
@@ -85,6 +98,12 @@
...
@@ -85,6 +98,12 @@
#include "libswresample/swresample.h"
#include "libswresample/swresample.h"
// deprecated features
#define FFMPEG_OPT_PSNR 1
#define FFMPEG_OPT_MAP_CHANNEL 1
#define FFMPEG_OPT_MAP_SYNC 1
#define FFMPEG_ROTATION_METADATA 1
enum
VideoSyncMethod
{
enum
VideoSyncMethod
{
VSYNC_AUTO
=
-
1
,
VSYNC_AUTO
=
-
1
,
VSYNC_PASSTHROUGH
,
VSYNC_PASSTHROUGH
,
...
@@ -113,15 +132,15 @@ typedef struct StreamMap {
...
@@ -113,15 +132,15 @@ typedef struct StreamMap {
int
disabled
;
/* 1 is this mapping is disabled by a negative map */
int
disabled
;
/* 1 is this mapping is disabled by a negative map */
int
file_index
;
int
file_index
;
int
stream_index
;
int
stream_index
;
int
sync_file_index
;
int
sync_stream_index
;
char
*
linklabel
;
/* name of an output link, for mapping lavfi outputs */
char
*
linklabel
;
/* name of an output link, for mapping lavfi outputs */
}
StreamMap
;
}
StreamMap
;
#if FFMPEG_OPT_MAP_CHANNEL
typedef
struct
{
typedef
struct
{
int
file_idx
,
stream_idx
,
channel_idx
;
// input
int
file_idx
,
stream_idx
,
channel_idx
;
// input
int
ofile_idx
,
ostream_idx
;
// output
int
ofile_idx
,
ostream_idx
;
// output
}
AudioChannelMap
;
}
AudioChannelMap
;
#endif
typedef
struct
OptionsContext
{
typedef
struct
OptionsContext
{
OptionGroup
*
g
;
OptionGroup
*
g
;
...
@@ -157,6 +176,7 @@ typedef struct OptionsContext {
...
@@ -157,6 +176,7 @@ typedef struct OptionsContext {
int
accurate_seek
;
int
accurate_seek
;
int
thread_queue_size
;
int
thread_queue_size
;
int
input_sync_ref
;
int
input_sync_ref
;
int
find_stream_info
;
SpecifierOpt
*
ts_scale
;
SpecifierOpt
*
ts_scale
;
int
nb_ts_scale
;
int
nb_ts_scale
;
...
@@ -174,11 +194,10 @@ typedef struct OptionsContext {
...
@@ -174,11 +194,10 @@ typedef struct OptionsContext {
/* output options */
/* output options */
StreamMap
*
stream_maps
;
StreamMap
*
stream_maps
;
int
nb_stream_maps
;
int
nb_stream_maps
;
#if FFMPEG_OPT_MAP_CHANNEL
AudioChannelMap
*
audio_channel_maps
;
/* one info entry per -map_channel */
AudioChannelMap
*
audio_channel_maps
;
/* one info entry per -map_channel */
int
nb_audio_channel_maps
;
/* number of (valid) -map_channel settings */
int
nb_audio_channel_maps
;
/* number of (valid) -map_channel settings */
int
metadata_global_manual
;
#endif
int
metadata_streams_manual
;
int
metadata_chapters_manual
;
const
char
**
attachments
;
const
char
**
attachments
;
int
nb_attachments
;
int
nb_attachments
;
...
@@ -186,9 +205,10 @@ typedef struct OptionsContext {
...
@@ -186,9 +205,10 @@ typedef struct OptionsContext {
int64_t
recording_time
;
int64_t
recording_time
;
int64_t
stop_time
;
int64_t
stop_time
;
u
int64_t
limit_filesize
;
int64_t
limit_filesize
;
float
mux_preload
;
float
mux_preload
;
float
mux_max_delay
;
float
mux_max_delay
;
float
shortest_buf_duration
;
int
shortest
;
int
shortest
;
int
bitexact
;
int
bitexact
;
...
@@ -221,6 +241,12 @@ typedef struct OptionsContext {
...
@@ -221,6 +241,12 @@ typedef struct OptionsContext {
int
nb_force_fps
;
int
nb_force_fps
;
SpecifierOpt
*
frame_aspect_ratios
;
SpecifierOpt
*
frame_aspect_ratios
;
int
nb_frame_aspect_ratios
;
int
nb_frame_aspect_ratios
;
SpecifierOpt
*
display_rotations
;
int
nb_display_rotations
;
SpecifierOpt
*
display_hflips
;
int
nb_display_hflips
;
SpecifierOpt
*
display_vflips
;
int
nb_display_vflips
;
SpecifierOpt
*
rc_overrides
;
SpecifierOpt
*
rc_overrides
;
int
nb_rc_overrides
;
int
nb_rc_overrides
;
SpecifierOpt
*
intra_matrices
;
SpecifierOpt
*
intra_matrices
;
...
@@ -247,6 +273,8 @@ typedef struct OptionsContext {
...
@@ -247,6 +273,8 @@ typedef struct OptionsContext {
int
nb_reinit_filters
;
int
nb_reinit_filters
;
SpecifierOpt
*
fix_sub_duration
;
SpecifierOpt
*
fix_sub_duration
;
int
nb_fix_sub_duration
;
int
nb_fix_sub_duration
;
SpecifierOpt
*
fix_sub_duration_heartbeat
;
int
nb_fix_sub_duration_heartbeat
;
SpecifierOpt
*
canvas_sizes
;
SpecifierOpt
*
canvas_sizes
;
int
nb_canvas_sizes
;
int
nb_canvas_sizes
;
SpecifierOpt
*
pass
;
SpecifierOpt
*
pass
;
...
@@ -275,6 +303,18 @@ typedef struct OptionsContext {
...
@@ -275,6 +303,18 @@ typedef struct OptionsContext {
int
nb_autoscale
;
int
nb_autoscale
;
SpecifierOpt
*
bits_per_raw_sample
;
SpecifierOpt
*
bits_per_raw_sample
;
int
nb_bits_per_raw_sample
;
int
nb_bits_per_raw_sample
;
SpecifierOpt
*
enc_stats_pre
;
int
nb_enc_stats_pre
;
SpecifierOpt
*
enc_stats_post
;
int
nb_enc_stats_post
;
SpecifierOpt
*
mux_stats
;
int
nb_mux_stats
;
SpecifierOpt
*
enc_stats_pre_fmt
;
int
nb_enc_stats_pre_fmt
;
SpecifierOpt
*
enc_stats_post_fmt
;
int
nb_enc_stats_post_fmt
;
SpecifierOpt
*
mux_stats_fmt
;
int
nb_mux_stats_fmt
;
}
OptionsContext
;
}
OptionsContext
;
typedef
struct
InputFilter
{
typedef
struct
InputFilter
{
...
@@ -350,12 +390,22 @@ typedef struct InputStream {
...
@@ -350,12 +390,22 @@ typedef struct InputStream {
#define DECODING_FOR_OST 1
#define DECODING_FOR_OST 1
#define DECODING_FOR_FILTER 2
#define DECODING_FOR_FILTER 2
int
processing_needed
;
/* non zero if the packets must be processed */
int
processing_needed
;
/* non zero if the packets must be processed */
// should attach FrameData as opaque_ref after decoding
int
want_frame_data
;
/**
* Codec parameters - to be used by the decoding/streamcopy code.
* st->codecpar should not be accessed, because it may be modified
* concurrently by the demuxing thread.
*/
AVCodecParameters
*
par
;
AVCodecContext
*
dec_ctx
;
AVCodecContext
*
dec_ctx
;
const
AVCodec
*
dec
;
const
AVCodec
*
dec
;
AVFrame
*
decoded_frame
;
AVFrame
*
decoded_frame
;
AVPacket
*
pkt
;
AVPacket
*
pkt
;
AVRational
framerate_guessed
;
int64_t
prev_pkt_pts
;
int64_t
prev_pkt_pts
;
int64_t
start
;
/* time when read started */
int64_t
start
;
/* time when read started */
/* predicted dts of the next packet read for this stream or (when there are
/* predicted dts of the next packet read for this stream or (when there are
...
@@ -368,6 +418,12 @@ typedef struct InputStream {
...
@@ -368,6 +418,12 @@ typedef struct InputStream {
int64_t
pts
;
///< current pts of the decoded frame (in AV_TIME_BASE units)
int64_t
pts
;
///< current pts of the decoded frame (in AV_TIME_BASE units)
int
wrap_correction_done
;
int
wrap_correction_done
;
// the value of AVCodecParserContext.repeat_pict from the AVStream parser
// for the last packet returned from ifile_get_packet()
// -1 if unknown
// FIXME: this is a hack, the avstream parser should not be used
int
last_pkt_repeat_pict
;
int64_t
filter_in_rescale_delta_last
;
int64_t
filter_in_rescale_delta_last
;
int64_t
min_pts
;
/* pts with the smallest value in a current stream */
int64_t
min_pts
;
/* pts with the smallest value in a current stream */
...
@@ -417,12 +473,8 @@ typedef struct InputStream {
...
@@ -417,12 +473,8 @@ typedef struct InputStream {
char
*
hwaccel_device
;
char
*
hwaccel_device
;
enum
AVPixelFormat
hwaccel_output_format
;
enum
AVPixelFormat
hwaccel_output_format
;
/* hwaccel context */
void
*
hwaccel_ctx
;
void
(
*
hwaccel_uninit
)(
AVCodecContext
*
s
);
int
(
*
hwaccel_retrieve_data
)(
AVCodecContext
*
s
,
AVFrame
*
frame
);
int
(
*
hwaccel_retrieve_data
)(
AVCodecContext
*
s
,
AVFrame
*
frame
);
enum
AVPixelFormat
hwaccel_pix_fmt
;
enum
AVPixelFormat
hwaccel_pix_fmt
;
enum
AVPixelFormat
hwaccel_retrieved_pix_fmt
;
/* stats */
/* stats */
// combined size of all the packets read
// combined size of all the packets read
...
@@ -439,38 +491,46 @@ typedef struct InputStream {
...
@@ -439,38 +491,46 @@ typedef struct InputStream {
int
got_output
;
int
got_output
;
}
InputStream
;
}
InputStream
;
typedef
struct
LastFrameDuration
{
int
stream_idx
;
int64_t
duration
;
}
LastFrameDuration
;
typedef
struct
InputFile
{
typedef
struct
InputFile
{
int
index
;
AVFormatContext
*
ctx
;
AVFormatContext
*
ctx
;
int
eof_reached
;
/* true if eof reached */
int
eof_reached
;
/* true if eof reached */
int
eagain
;
/* true if last read attempt returned EAGAIN */
int
eagain
;
/* true if last read attempt returned EAGAIN */
int
ist_index
;
/* index of first stream in input_streams */
int
loop
;
/* set number of times input stream should be looped */
int64_t
duration
;
/* actual duration of the longest stream in a file
at the moment when looping happens */
AVRational
time_base
;
/* time base of the duration */
int64_t
input_ts_offset
;
int64_t
input_ts_offset
;
int
input_sync_ref
;
int
input_sync_ref
;
/**
* Effective format start time based on enabled streams.
*/
int64_t
start_time_effective
;
int64_t
ts_offset
;
int64_t
ts_offset
;
/**
* Extra timestamp offset added by discontinuity handling.
*/
int64_t
ts_offset_discont
;
int64_t
last_ts
;
int64_t
last_ts
;
int64_t
start_time
;
/* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */
int64_t
start_time
;
/* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */
int64_t
recording_time
;
int64_t
recording_time
;
int
nb_streams
;
/* number of stream that ffmpeg is aware of; may be different
from ctx.nb_streams if new streams appear during av_read_frame() */
/* streams that ffmpeg is aware of;
int
nb_streams_warn
;
/* number of streams that the user was warned of */
* there may be extra streams in ctx that are not mapped to an InputStream
* if new streams appear dynamically during demuxing */
InputStream
**
streams
;
int
nb_streams
;
int
rate_emu
;
int
rate_emu
;
float
readrate
;
float
readrate
;
int
accurate_seek
;
int
accurate_seek
;
AVPacket
*
pkt
;
/* when looping the input file, this queue is used by decoders to report
* the last frame duration back to the demuxer thread */
#if HAVE_THREADS
AVThreadMessageQueue
*
audio_duration_queue
;
AVThreadMessageQueue
*
in_thread_queue
;
int
audio_duration_queue_size
;
pthread_t
thread
;
/* thread reading from this file */
int
non_blocking
;
/* reading packets from the thread should not block */
int
joined
;
/* the thread has been joined */
int
thread_queue_size
;
/* maximum number of queued packets */
#endif
}
InputFile
;
}
InputFile
;
enum
forced_keyframes_const
{
enum
forced_keyframes_const
{
...
@@ -485,6 +545,41 @@ enum forced_keyframes_const {
...
@@ -485,6 +545,41 @@ enum forced_keyframes_const {
#define ABORT_ON_FLAG_EMPTY_OUTPUT (1 << 0)
#define ABORT_ON_FLAG_EMPTY_OUTPUT (1 << 0)
#define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 << 1)
#define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 << 1)
enum
EncStatsType
{
ENC_STATS_LITERAL
=
0
,
ENC_STATS_FILE_IDX
,
ENC_STATS_STREAM_IDX
,
ENC_STATS_FRAME_NUM
,
ENC_STATS_FRAME_NUM_IN
,
ENC_STATS_TIMEBASE
,
ENC_STATS_TIMEBASE_IN
,
ENC_STATS_PTS
,
ENC_STATS_PTS_TIME
,
ENC_STATS_PTS_IN
,
ENC_STATS_PTS_TIME_IN
,
ENC_STATS_DTS
,
ENC_STATS_DTS_TIME
,
ENC_STATS_SAMPLE_NUM
,
ENC_STATS_NB_SAMPLES
,
ENC_STATS_PKT_SIZE
,
ENC_STATS_BITRATE
,
ENC_STATS_AVG_BITRATE
,
};
typedef
struct
EncStatsComponent
{
enum
EncStatsType
type
;
uint8_t
*
str
;
size_t
str_len
;
}
EncStatsComponent
;
typedef
struct
EncStats
{
EncStatsComponent
*
components
;
int
nb_components
;
AVIOContext
*
io
;
}
EncStats
;
extern
const
char
*
const
forced_keyframes_const_names
[];
extern
const
char
*
const
forced_keyframes_const_names
[];
typedef
enum
{
typedef
enum
{
...
@@ -492,68 +587,92 @@ typedef enum {
...
@@ -492,68 +587,92 @@ typedef enum {
MUXER_FINISHED
=
2
,
MUXER_FINISHED
=
2
,
}
OSTFinished
;
}
OSTFinished
;
enum
{
KF_FORCE_SOURCE
=
1
,
KF_FORCE_SOURCE_NO_DROP
=
2
,
};
typedef
struct
KeyframeForceCtx
{
int
type
;
int64_t
ref_pts
;
// timestamps of the forced keyframes, in AV_TIME_BASE_Q
int64_t
*
pts
;
int
nb_pts
;
int
index
;
AVExpr
*
pexpr
;
double
expr_const_values
[
FKF_NB
];
int
dropped_keyframe
;
}
KeyframeForceCtx
;
typedef
struct
OutputStream
{
typedef
struct
OutputStream
{
const
AVClass
*
clazz
;
int
file_index
;
/* file index */
int
file_index
;
/* file index */
int
index
;
/* stream index in the output file */
int
index
;
/* stream index in the output file */
int
source_index
;
/* InputStream index */
/* input stream that is the source for this output stream;
* may be NULL for streams with no well-defined source, e.g.
* attachments or outputs from complex filtergraphs */
InputStream
*
ist
;
AVStream
*
st
;
/* stream in the output file */
AVStream
*
st
;
/* stream in the output file */
int
encoding_needed
;
/* true if encoding needed for this stream */
/* number of frames emitted by the video-encoding sync code */
int64_t
frame_number
;
int64_t
vsync_frame_number
;
/* input pts and corresponding output pts
/* predicted pts of the next frame to be encoded
for A/V sync */
* audio/video encoding only */
struct
InputStream
*
sync_ist
;
/* input stream to sync against */
int64_t
next_pts
;
int64_t
sync_opts
;
/* output frame counter, could be changed to some true timestamp */
// FIXME look at frame_number
/* dts of the last packet sent to the muxing queue, in AV_TIME_BASE_Q */
/* pts of the first frame encoded for this stream, used for limiting
* recording time */
int64_t
first_pts
;
/* dts of the last packet sent to the muxer */
int64_t
last_mux_dts
;
int64_t
last_mux_dts
;
/* pts of the last frame received from the filters, in AV_TIME_BASE_Q */
int64_t
last_filter_pts
;
// timestamp from which the streamcopied streams should start,
// in AV_TIME_BASE_Q;
// everything before it should be discarded
int64_t
ts_copy_start
;
// the timebase of the packets sent to the muxer
// the timebase of the packets sent to the muxer
AVRational
mux_timebase
;
AVRational
mux_timebase
;
AVRational
enc_timebase
;
AVRational
enc_timebase
;
AVBSFContext
*
bsf_ctx
;
AVCodecContext
*
enc_ctx
;
AVCodecContext
*
enc_ctx
;
AVCodecParameters
*
ref_par
;
/* associated input codec parameters with encoders options applied */
const
AVCodec
*
enc
;
int64_t
max_frames
;
AVFrame
*
filtered_frame
;
AVFrame
*
filtered_frame
;
AVFrame
*
last_frame
;
AVFrame
*
last_frame
;
AVFrame
*
sq_frame
;
AVPacket
*
pkt
;
AVPacket
*
pkt
;
int64_t
last_dropped
;
int64_t
last_dropped
;
int64_t
last_nb0_frames
[
3
];
int64_t
last_nb0_frames
[
3
];
void
*
hwaccel_ctx
;
/* video only */
/* video only */
AVRational
frame_rate
;
AVRational
frame_rate
;
AVRational
max_frame_rate
;
AVRational
max_frame_rate
;
enum
VideoSyncMethod
vsync_method
;
enum
VideoSyncMethod
vsync_method
;
int
is_cfr
;
int
is_cfr
;
const
char
*
fps_mode
;
int
force_fps
;
int
force_fps
;
int
top_field_first
;
int
top_field_first
;
#if FFMPEG_ROTATION_METADATA
int
rotate_overridden
;
int
rotate_overridden
;
#endif
int
autoscale
;
int
autoscale
;
int
bitexact
;
int
bits_per_raw_sample
;
int
bits_per_raw_sample
;
#if FFMPEG_ROTATION_METADATA
double
rotate_override_value
;
double
rotate_override_value
;
#endif
AVRational
frame_aspect_ratio
;
AVRational
frame_aspect_ratio
;
/* forced key frames */
KeyframeForceCtx
kf
;
int64_t
forced_kf_ref_pts
;
int64_t
*
forced_kf_pts
;
int
forced_kf_count
;
int
forced_kf_index
;
char
*
forced_keyframes
;
AVExpr
*
forced_keyframes_pexpr
;
double
forced_keyframes_expr_const_values
[
FKF_NB
];
int
dropped_keyframe
;
/* audio only */
/* audio only */
#if FFMPEG_OPT_MAP_CHANNEL
int
*
audio_channels_map
;
/* list of the channels id to pick from the source stream */
int
*
audio_channels_map
;
/* list of the channels id to pick from the source stream */
int
audio_channels_mapped
;
/* number of channels in audio_channels_map */
int
audio_channels_mapped
;
/* number of channels in audio_channels_map */
#endif
char
*
logfile_prefix
;
char
*
logfile_prefix
;
FILE
*
logfile
;
FILE
*
logfile
;
...
@@ -569,7 +688,6 @@ typedef struct OutputStream {
...
@@ -569,7 +688,6 @@ typedef struct OutputStream {
char
*
apad
;
char
*
apad
;
OSTFinished
finished
;
/* no more packets should be written for this stream */
OSTFinished
finished
;
/* no more packets should be written for this stream */
int
unavailable
;
/* true if the steram is unavailable (possibly temporarily) */
int
unavailable
;
/* true if the steram is unavailable (possibly temporarily) */
int
stream_copy
;
// init_output_stream() has been called for this stream
// init_output_stream() has been called for this stream
// The encoder and the bitstream filters have been initialized and the stream
// The encoder and the bitstream filters have been initialized and the stream
...
@@ -582,15 +700,16 @@ typedef struct OutputStream {
...
@@ -582,15 +700,16 @@ typedef struct OutputStream {
int
streamcopy_started
;
int
streamcopy_started
;
int
copy_initial_nonkeyframes
;
int
copy_initial_nonkeyframes
;
int
copy_prior_start
;
int
copy_prior_start
;
char
*
disposition
;
int
keep_pix_fmt
;
int
keep_pix_fmt
;
/* stats */
/* stats */
// combined size of all the packets written
// combined size of all the packets sent to the muxer
uint64_t
data_size
;
uint64_t
data_size_mux
;
// combined size of all the packets received from the encoder
uint64_t
data_size_enc
;
// number of packets send to the muxer
// number of packets send to the muxer
uin
t64_t
packets_written
;
atomic_uint_leas
t64_t
packets_written
;
// number of frames/samples sent to the encoder
// number of frames/samples sent to the encoder
uint64_t
frames_encoded
;
uint64_t
frames_encoded
;
uint64_t
samples_encoded
;
uint64_t
samples_encoded
;
...
@@ -600,51 +719,48 @@ typedef struct OutputStream {
...
@@ -600,51 +719,48 @@ typedef struct OutputStream {
/* packet quality factor */
/* packet quality factor */
int
quality
;
int
quality
;
int
max_muxing_queue_size
;
/* the packets are buffered here until the muxer is ready to be initialized */
AVFifo
*
muxing_queue
;
/*
* The size of the AVPackets' buffers in queue.
* Updated when a packet is either pushed or pulled from the queue.
*/
size_t
muxing_queue_data_size
;
/* Threshold after which max_muxing_queue_size will be in effect */
size_t
muxing_queue_data_threshold
;
/* packet picture type */
/* packet picture type */
int
pict_type
;
int
pict_type
;
/* frame encode sum of squared error values */
/* frame encode sum of squared error values */
int64_t
error
[
4
];
int64_t
error
[
4
];
int
sq_idx_encode
;
int
sq_idx_mux
;
EncStats
enc_stats_pre
;
EncStats
enc_stats_post
;
/*
* bool on whether this stream should be utilized for splitting
* subtitles utilizing fix_sub_duration at random access points.
*/
unsigned
int
fix_sub_duration_heartbeat
;
}
OutputStream
;
}
OutputStream
;
typedef
struct
OutputFile
{
typedef
struct
OutputFile
{
const
AVClass
*
clazz
;
int
index
;
int
index
;
const
AVOutputFormat
*
format
;
const
AVOutputFormat
*
format
;
const
char
*
url
;
OutputStream
**
streams
;
int
nb_streams
;
SyncQueue
*
sq_encode
;
AVFormatContext
*
ctx
;
AVDictionary
*
opts
;
int
ost_index
;
/* index of the first stream in output_streams */
int64_t
recording_time
;
///< desired length of the resulting file in microseconds == AV_TIME_BASE units
int64_t
recording_time
;
///< desired length of the resulting file in microseconds == AV_TIME_BASE units
int64_t
start_time
;
///< start time in microseconds == AV_TIME_BASE units
int64_t
start_time
;
///< start time in microseconds == AV_TIME_BASE units
uint64_t
limit_filesize
;
/* filesize limit expressed in bytes */
int
shortest
;
int
shortest
;
int
bitexact
;
int
header_written
;
}
OutputFile
;
}
OutputFile
;
extern
__thread
InputStream
**
input_streams
;
extern
__thread
int
nb_input_streams
;
extern
__thread
InputFile
**
input_files
;
extern
__thread
InputFile
**
input_files
;
extern
__thread
int
nb_input_files
;
extern
__thread
int
nb_input_files
;
extern
__thread
OutputStream
**
output_streams
;
extern
__thread
int
nb_output_streams
;
extern
__thread
OutputFile
**
output_files
;
extern
__thread
OutputFile
**
output_files
;
extern
__thread
int
nb_output_files
;
extern
__thread
int
nb_output_files
;
...
@@ -658,13 +774,10 @@ extern __thread float audio_drift_threshold;
...
@@ -658,13 +774,10 @@ extern __thread float audio_drift_threshold;
extern
__thread
float
dts_delta_threshold
;
extern
__thread
float
dts_delta_threshold
;
extern
__thread
float
dts_error_threshold
;
extern
__thread
float
dts_error_threshold
;
extern
__thread
int
audio_volume
;
extern
__thread
int
audio_sync_method
;
extern
__thread
enum
VideoSyncMethod
video_sync_method
;
extern
__thread
enum
VideoSyncMethod
video_sync_method
;
extern
__thread
float
frame_drop_threshold
;
extern
__thread
float
frame_drop_threshold
;
extern
__thread
int
do_benchmark
;
extern
__thread
int
do_benchmark
;
extern
__thread
int
do_benchmark_all
;
extern
__thread
int
do_benchmark_all
;
extern
__thread
int
do_deinterlace
;
extern
__thread
int
do_hex_dump
;
extern
__thread
int
do_hex_dump
;
extern
__thread
int
do_pkt_dump
;
extern
__thread
int
do_pkt_dump
;
extern
__thread
int
copy_ts
;
extern
__thread
int
copy_ts
;
...
@@ -677,7 +790,6 @@ extern __thread int print_stats;
...
@@ -677,7 +790,6 @@ extern __thread int print_stats;
extern
__thread
int64_t
stats_period
;
extern
__thread
int64_t
stats_period
;
extern
__thread
int
qp_hist
;
extern
__thread
int
qp_hist
;
extern
__thread
int
stdin_interaction
;
extern
__thread
int
stdin_interaction
;
extern
__thread
int
frame_bits_per_raw_sample
;
extern
__thread
AVIOContext
*
progress_avio
;
extern
__thread
AVIOContext
*
progress_avio
;
extern
__thread
float
max_error_rate
;
extern
__thread
float
max_error_rate
;
...
@@ -688,15 +800,20 @@ extern __thread int auto_conversion_filters;
...
@@ -688,15 +800,20 @@ extern __thread int auto_conversion_filters;
extern
__thread
const
AVIOInterruptCB
int_cb
;
extern
__thread
const
AVIOInterruptCB
int_cb
;
#if CONFIG_QSV
extern
__thread
char
*
qsv_device
;
#endif
extern
__thread
HWDevice
*
filter_hw_device
;
extern
__thread
HWDevice
*
filter_hw_device
;
extern
__thread
int
want_sdp
;
extern
__thread
unsigned
nb_output_dumped
;
extern
__thread
unsigned
nb_output_dumped
;
extern
__thread
int
main_ffmpeg_return_code
;
extern
__thread
int
main_ffmpeg_return_code
;
extern
__thread
int
ignore_unknown_streams
;
extern
__thread
int
copy_unknown_streams
;
extern
__thread
int
recast_media
;
#if FFMPEG_OPT_PSNR
extern
__thread
int
do_psnr
;
#endif
void
term_init
(
void
);
void
term_init
(
void
);
void
term_exit
(
void
);
void
term_exit
(
void
);
...
@@ -705,7 +822,12 @@ void show_usage(void);
...
@@ -705,7 +822,12 @@ void show_usage(void);
void
remove_avoptions
(
AVDictionary
**
a
,
AVDictionary
*
b
);
void
remove_avoptions
(
AVDictionary
**
a
,
AVDictionary
*
b
);
void
assert_avoptions
(
AVDictionary
*
m
);
void
assert_avoptions
(
AVDictionary
*
m
);
int
guess_input_channel_layout
(
InputStream
*
ist
);
void
assert_file_overwrite
(
const
char
*
filename
);
char
*
file_read
(
const
char
*
filename
);
AVDictionary
*
strip_specifiers
(
const
AVDictionary
*
dict
);
const
AVCodec
*
find_codec_or_die
(
void
*
logctx
,
const
char
*
name
,
enum
AVMediaType
type
,
int
encoder
);
int
parse_and_set_vsync
(
const
char
*
arg
,
int
*
vsync_var
,
int
file_idx
,
int
st_idx
,
int
is_global
);
int
configure_filtergraph
(
FilterGraph
*
fg
);
int
configure_filtergraph
(
FilterGraph
*
fg
);
void
check_filter_outputs
(
void
);
void
check_filter_outputs
(
void
);
...
@@ -719,8 +841,9 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
...
@@ -719,8 +841,9 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
int
ffmpeg_parse_options
(
int
argc
,
char
**
argv
);
int
ffmpeg_parse_options
(
int
argc
,
char
**
argv
);
int
videotoolbox_init
(
AVCodecContext
*
s
);
void
enc_stats_write
(
OutputStream
*
ost
,
EncStats
*
es
,
int
qsv_init
(
AVCodecContext
*
s
);
const
AVFrame
*
frame
,
const
AVPacket
*
pkt
,
uint64_t
frame_num
);
HWDevice
*
hw_device_get_by_name
(
const
char
*
name
);
HWDevice
*
hw_device_get_by_name
(
const
char
*
name
);
int
hw_device_init_from_string
(
const
char
*
arg
,
HWDevice
**
dev
);
int
hw_device_init_from_string
(
const
char
*
arg
,
HWDevice
**
dev
);
...
@@ -732,15 +855,58 @@ int hw_device_setup_for_filter(FilterGraph *fg);
...
@@ -732,15 +855,58 @@ int hw_device_setup_for_filter(FilterGraph *fg);
int
hwaccel_decode_init
(
AVCodecContext
*
avctx
);
int
hwaccel_decode_init
(
AVCodecContext
*
avctx
);
/* open the muxer when all the streams are initialized */
/*
int
of_check_init
(
OutputFile
*
of
);
* Initialize muxing state for the given stream, should be called
* after the codec/streamcopy setup has been done.
*
* Open the muxer once all the streams have been initialized.
*/
int
of_stream_init
(
OutputFile
*
of
,
OutputStream
*
ost
);
int
of_write_trailer
(
OutputFile
*
of
);
int
of_write_trailer
(
OutputFile
*
of
);
int
of_open
(
const
OptionsContext
*
o
,
const
char
*
filename
);
void
of_close
(
OutputFile
**
pof
);
void
of_close
(
OutputFile
**
pof
);
void
of_write_packet
(
OutputFile
*
of
,
AVPacket
*
pkt
,
OutputStream
*
ost
,
void
of_enc_stats_close
(
void
);
int
unqueue
);
/*
* Send a single packet to the output, applying any bitstream filters
* associated with the output stream. This may result in any number
* of packets actually being written, depending on what bitstream
* filters are applied. The supplied packet is consumed and will be
* blank (as if newly-allocated) when this function returns.
*
* If eof is set, instead indicate EOF to all bitstream filters and
* therefore flush any delayed packets to the output. A blank packet
* must be supplied in this case.
*/
void
of_output_packet
(
OutputFile
*
of
,
AVPacket
*
pkt
,
OutputStream
*
ost
,
int
eof
);
int64_t
of_filesize
(
OutputFile
*
of
);
int
ifile_open
(
const
OptionsContext
*
o
,
const
char
*
filename
);
void
ifile_close
(
InputFile
**
f
);
/**
* Get next input packet from the demuxer.
*
* @param pkt the packet is written here when this function returns 0
* @return
* - 0 when a packet has been read successfully
* - 1 when stream end was reached, but the stream is looped;
* caller should flush decoders and read from this demuxer again
* - a negative error code on failure
*/
int
ifile_get_packet
(
InputFile
*
f
,
AVPacket
**
pkt
);
/* iterate over all input streams in all input files;
* pass NULL to start iteration */
InputStream
*
ist_iter
(
InputStream
*
prev
);
extern
const
char
*
const
opt_name_codec_names
[];
extern
const
char
*
const
opt_name_codec_tags
[];
extern
const
char
*
const
opt_name_frame_rates
[];
extern
const
char
*
const
opt_name_top_field_first
[];
void
set_report_callback
(
void
(
*
callback
)(
int
,
float
,
float
,
int64_t
,
int
,
double
,
double
));
void
set_report_callback
(
void
(
*
callback
)(
int
,
float
,
float
,
int64_t
,
double
,
double
,
double
));
void
cancel_operation
(
long
id
);
void
cancel_operation
(
long
id
);
#endif
/* FFTOOLS_FFMPEG_H */
#endif
/* FFTOOLS_FFMPEG_H */
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_demux.c
0 → 100644
浏览文件 @
f23762b9
/*
* Copyright (c) 2023 ARTHENICA LTD
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of ffmpeg_demux.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools_ prefix added to the file name
* - fftools_ffmpeg_mux.h include added
*/
#include <float.h>
#include <stdint.h>
#include "fftools_ffmpeg.h"
#include "fftools_ffmpeg_mux.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/display.h"
#include "libavutil/error.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/time.h"
#include "libavutil/timestamp.h"
#include "libavutil/thread.h"
#include "libavutil/threadmessage.h"
#include "libavcodec/packet.h"
#include "libavformat/avformat.h"
static
const
char
*
const
opt_name_discard
[]
=
{
"discard"
,
NULL
};
static
const
char
*
const
opt_name_reinit_filters
[]
=
{
"reinit_filter"
,
NULL
};
static
const
char
*
const
opt_name_fix_sub_duration
[]
=
{
"fix_sub_duration"
,
NULL
};
static
const
char
*
const
opt_name_canvas_sizes
[]
=
{
"canvas_size"
,
NULL
};
static
const
char
*
const
opt_name_guess_layout_max
[]
=
{
"guess_layout_max"
,
NULL
};
static
const
char
*
const
opt_name_ts_scale
[]
=
{
"itsscale"
,
NULL
};
static
const
char
*
const
opt_name_hwaccels
[]
=
{
"hwaccel"
,
NULL
};
static
const
char
*
const
opt_name_hwaccel_devices
[]
=
{
"hwaccel_device"
,
NULL
};
static
const
char
*
const
opt_name_hwaccel_output_formats
[]
=
{
"hwaccel_output_format"
,
NULL
};
static
const
char
*
const
opt_name_autorotate
[]
=
{
"autorotate"
,
NULL
};
static
const
char
*
const
opt_name_display_rotations
[]
=
{
"display_rotation"
,
NULL
};
static
const
char
*
const
opt_name_display_hflips
[]
=
{
"display_hflip"
,
NULL
};
static
const
char
*
const
opt_name_display_vflips
[]
=
{
"display_vflip"
,
NULL
};
typedef
struct
Demuxer
{
InputFile
f
;
/* number of times input stream should be looped */
int
loop
;
/* actual duration of the longest stream in a file at the moment when
* looping happens */
int64_t
duration
;
/* time base of the duration */
AVRational
time_base
;
/* number of streams that the user was warned of */
int
nb_streams_warn
;
AVThreadMessageQueue
*
in_thread_queue
;
int
thread_queue_size
;
pthread_t
thread
;
int
non_blocking
;
}
Demuxer
;
typedef
struct
DemuxMsg
{
AVPacket
*
pkt
;
int
looping
;
// repeat_pict from the demuxer-internal parser
int
repeat_pict
;
}
DemuxMsg
;
static
Demuxer
*
demuxer_from_ifile
(
InputFile
*
f
)
{
return
(
Demuxer
*
)
f
;
}
static
void
report_new_stream
(
Demuxer
*
d
,
const
AVPacket
*
pkt
)
{
AVStream
*
st
=
d
->
f
.
ctx
->
streams
[
pkt
->
stream_index
];
if
(
pkt
->
stream_index
<
d
->
nb_streams_warn
)
return
;
av_log
(
NULL
,
AV_LOG_WARNING
,
"New %s stream %d:%d at pos:%"
PRId64
" and DTS:%ss
\n
"
,
av_get_media_type_string
(
st
->
codecpar
->
codec_type
),
d
->
f
.
index
,
pkt
->
stream_index
,
pkt
->
pos
,
av_ts2timestr
(
pkt
->
dts
,
&
st
->
time_base
));
d
->
nb_streams_warn
=
pkt
->
stream_index
+
1
;
}
static
void
ifile_duration_update
(
Demuxer
*
d
,
InputStream
*
ist
,
int64_t
last_duration
)
{
/* the total duration of the stream, max_pts - min_pts is
* the duration of the stream without the last frame */
if
(
ist
->
max_pts
>
ist
->
min_pts
&&
ist
->
max_pts
-
(
uint64_t
)
ist
->
min_pts
<
INT64_MAX
-
last_duration
)
last_duration
+=
ist
->
max_pts
-
ist
->
min_pts
;
if
(
!
d
->
duration
||
av_compare_ts
(
d
->
duration
,
d
->
time_base
,
last_duration
,
ist
->
st
->
time_base
)
<
0
)
{
d
->
duration
=
last_duration
;
d
->
time_base
=
ist
->
st
->
time_base
;
}
}
static
int
seek_to_start
(
Demuxer
*
d
)
{
InputFile
*
ifile
=
&
d
->
f
;
AVFormatContext
*
is
=
ifile
->
ctx
;
InputStream
*
ist
;
int
ret
;
ret
=
avformat_seek_file
(
is
,
-
1
,
INT64_MIN
,
is
->
start_time
,
is
->
start_time
,
0
);
if
(
ret
<
0
)
return
ret
;
if
(
ifile
->
audio_duration_queue_size
)
{
/* duration is the length of the last frame in a stream
* when audio stream is present we don't care about
* last video frame length because it's not defined exactly */
int
got_durations
=
0
;
while
(
got_durations
<
ifile
->
audio_duration_queue_size
)
{
LastFrameDuration
dur
;
ret
=
av_thread_message_queue_recv
(
ifile
->
audio_duration_queue
,
&
dur
,
0
);
if
(
ret
<
0
)
return
ret
;
got_durations
++
;
ist
=
ifile
->
streams
[
dur
.
stream_idx
];
ifile_duration_update
(
d
,
ist
,
dur
.
duration
);
}
}
else
{
for
(
int
i
=
0
;
i
<
ifile
->
nb_streams
;
i
++
)
{
int64_t
duration
=
0
;
ist
=
ifile
->
streams
[
i
];
if
(
ist
->
framerate
.
num
)
{
duration
=
av_rescale_q
(
1
,
av_inv_q
(
ist
->
framerate
),
ist
->
st
->
time_base
);
}
else
if
(
ist
->
st
->
avg_frame_rate
.
num
)
{
duration
=
av_rescale_q
(
1
,
av_inv_q
(
ist
->
st
->
avg_frame_rate
),
ist
->
st
->
time_base
);
}
else
{
duration
=
1
;
}
ifile_duration_update
(
d
,
ist
,
duration
);
}
}
if
(
d
->
loop
>
0
)
d
->
loop
--
;
return
ret
;
}
static
void
ts_fixup
(
Demuxer
*
d
,
AVPacket
*
pkt
,
int
*
repeat_pict
)
{
InputFile
*
ifile
=
&
d
->
f
;
InputStream
*
ist
=
ifile
->
streams
[
pkt
->
stream_index
];
const
int64_t
start_time
=
ifile
->
start_time_effective
;
int64_t
duration
;
if
(
debug_ts
)
{
av_log
(
NULL
,
AV_LOG_INFO
,
"demuxer -> ist_index:%d:%d type:%s "
"pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s
\n
"
,
ifile
->
index
,
pkt
->
stream_index
,
av_get_media_type_string
(
ist
->
st
->
codecpar
->
codec_type
),
av_ts2str
(
pkt
->
pts
),
av_ts2timestr
(
pkt
->
pts
,
&
ist
->
st
->
time_base
),
av_ts2str
(
pkt
->
dts
),
av_ts2timestr
(
pkt
->
dts
,
&
ist
->
st
->
time_base
),
av_ts2str
(
pkt
->
duration
),
av_ts2timestr
(
pkt
->
duration
,
&
ist
->
st
->
time_base
));
}
if
(
!
ist
->
wrap_correction_done
&&
start_time
!=
AV_NOPTS_VALUE
&&
ist
->
st
->
pts_wrap_bits
<
64
)
{
int64_t
stime
,
stime2
;
stime
=
av_rescale_q
(
start_time
,
AV_TIME_BASE_Q
,
ist
->
st
->
time_base
);
stime2
=
stime
+
(
1ULL
<<
ist
->
st
->
pts_wrap_bits
);
ist
->
wrap_correction_done
=
1
;
if
(
stime2
>
stime
&&
pkt
->
dts
!=
AV_NOPTS_VALUE
&&
pkt
->
dts
>
stime
+
(
1LL
<<
(
ist
->
st
->
pts_wrap_bits
-
1
)))
{
pkt
->
dts
-=
1ULL
<<
ist
->
st
->
pts_wrap_bits
;
ist
->
wrap_correction_done
=
0
;
}
if
(
stime2
>
stime
&&
pkt
->
pts
!=
AV_NOPTS_VALUE
&&
pkt
->
pts
>
stime
+
(
1LL
<<
(
ist
->
st
->
pts_wrap_bits
-
1
)))
{
pkt
->
pts
-=
1ULL
<<
ist
->
st
->
pts_wrap_bits
;
ist
->
wrap_correction_done
=
0
;
}
}
if
(
pkt
->
dts
!=
AV_NOPTS_VALUE
)
pkt
->
dts
+=
av_rescale_q
(
ifile
->
ts_offset
,
AV_TIME_BASE_Q
,
ist
->
st
->
time_base
);
if
(
pkt
->
pts
!=
AV_NOPTS_VALUE
)
pkt
->
pts
+=
av_rescale_q
(
ifile
->
ts_offset
,
AV_TIME_BASE_Q
,
ist
->
st
->
time_base
);
if
(
pkt
->
pts
!=
AV_NOPTS_VALUE
)
pkt
->
pts
*=
ist
->
ts_scale
;
if
(
pkt
->
dts
!=
AV_NOPTS_VALUE
)
pkt
->
dts
*=
ist
->
ts_scale
;
duration
=
av_rescale_q
(
d
->
duration
,
d
->
time_base
,
ist
->
st
->
time_base
);
if
(
pkt
->
pts
!=
AV_NOPTS_VALUE
)
{
pkt
->
pts
+=
duration
;
ist
->
max_pts
=
FFMAX
(
pkt
->
pts
,
ist
->
max_pts
);
ist
->
min_pts
=
FFMIN
(
pkt
->
pts
,
ist
->
min_pts
);
}
if
(
pkt
->
dts
!=
AV_NOPTS_VALUE
)
pkt
->
dts
+=
duration
;
*
repeat_pict
=
-
1
;
if
(
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
&&
av_stream_get_parser
(
ist
->
st
))
*
repeat_pict
=
av_stream_get_parser
(
ist
->
st
)
->
repeat_pict
;
}
static
void
thread_set_name
(
InputFile
*
f
)
{
char
name
[
16
];
snprintf
(
name
,
sizeof
(
name
),
"dmx%d:%s"
,
f
->
index
,
f
->
ctx
->
iformat
->
name
);
ff_thread_setname
(
name
);
}
static
void
*
input_thread
(
void
*
arg
)
{
Demuxer
*
d
=
arg
;
InputFile
*
f
=
&
d
->
f
;
AVPacket
*
pkt
;
unsigned
flags
=
d
->
non_blocking
?
AV_THREAD_MESSAGE_NONBLOCK
:
0
;
int
ret
=
0
;
pkt
=
av_packet_alloc
();
if
(
!
pkt
)
{
ret
=
AVERROR
(
ENOMEM
);
goto
finish
;
}
thread_set_name
(
f
);
while
(
1
)
{
DemuxMsg
msg
=
{
NULL
};
ret
=
av_read_frame
(
f
->
ctx
,
pkt
);
if
(
ret
==
AVERROR
(
EAGAIN
))
{
av_usleep
(
10000
);
continue
;
}
if
(
ret
<
0
)
{
if
(
d
->
loop
)
{
/* signal looping to the consumer thread */
msg
.
looping
=
1
;
ret
=
av_thread_message_queue_send
(
d
->
in_thread_queue
,
&
msg
,
0
);
if
(
ret
>=
0
)
ret
=
seek_to_start
(
d
);
if
(
ret
>=
0
)
continue
;
/* fallthrough to the error path */
}
if
(
ret
==
AVERROR_EOF
)
av_log
(
NULL
,
AV_LOG_VERBOSE
,
"EOF in input file %d
\n
"
,
f
->
index
);
else
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error demuxing input file %d: %s
\n
"
,
f
->
index
,
av_err2str
(
ret
));
break
;
}
if
(
do_pkt_dump
)
{
av_pkt_dump_log2
(
NULL
,
AV_LOG_INFO
,
pkt
,
do_hex_dump
,
f
->
ctx
->
streams
[
pkt
->
stream_index
]);
}
/* the following test is needed in case new streams appear
dynamically in stream : we ignore them */
if
(
pkt
->
stream_index
>=
f
->
nb_streams
)
{
report_new_stream
(
d
,
pkt
);
av_packet_unref
(
pkt
);
continue
;
}
if
(
pkt
->
flags
&
AV_PKT_FLAG_CORRUPT
)
{
av_log
(
NULL
,
exit_on_error
?
AV_LOG_FATAL
:
AV_LOG_WARNING
,
"%s: corrupt input packet in stream %d
\n
"
,
f
->
ctx
->
url
,
pkt
->
stream_index
);
if
(
exit_on_error
)
{
av_packet_unref
(
pkt
);
ret
=
AVERROR_INVALIDDATA
;
break
;
}
}
ts_fixup
(
d
,
pkt
,
&
msg
.
repeat_pict
);
msg
.
pkt
=
av_packet_alloc
();
if
(
!
msg
.
pkt
)
{
av_packet_unref
(
pkt
);
ret
=
AVERROR
(
ENOMEM
);
break
;
}
av_packet_move_ref
(
msg
.
pkt
,
pkt
);
ret
=
av_thread_message_queue_send
(
d
->
in_thread_queue
,
&
msg
,
flags
);
if
(
flags
&&
ret
==
AVERROR
(
EAGAIN
))
{
flags
=
0
;
ret
=
av_thread_message_queue_send
(
d
->
in_thread_queue
,
&
msg
,
flags
);
av_log
(
f
->
ctx
,
AV_LOG_WARNING
,
"Thread message queue blocking; consider raising the "
"thread_queue_size option (current value: %d)
\n
"
,
d
->
thread_queue_size
);
}
if
(
ret
<
0
)
{
if
(
ret
!=
AVERROR_EOF
)
av_log
(
f
->
ctx
,
AV_LOG_ERROR
,
"Unable to send packet to main thread: %s
\n
"
,
av_err2str
(
ret
));
av_packet_free
(
&
msg
.
pkt
);
break
;
}
}
finish:
av_assert0
(
ret
<
0
);
av_thread_message_queue_set_err_recv
(
d
->
in_thread_queue
,
ret
);
av_packet_free
(
&
pkt
);
av_log
(
NULL
,
AV_LOG_VERBOSE
,
"Terminating demuxer thread %d
\n
"
,
f
->
index
);
return
NULL
;
}
static
void
thread_stop
(
Demuxer
*
d
)
{
InputFile
*
f
=
&
d
->
f
;
DemuxMsg
msg
;
if
(
!
d
->
in_thread_queue
)
return
;
av_thread_message_queue_set_err_send
(
d
->
in_thread_queue
,
AVERROR_EOF
);
while
(
av_thread_message_queue_recv
(
d
->
in_thread_queue
,
&
msg
,
0
)
>=
0
)
av_packet_free
(
&
msg
.
pkt
);
pthread_join
(
d
->
thread
,
NULL
);
av_thread_message_queue_free
(
&
d
->
in_thread_queue
);
av_thread_message_queue_free
(
&
f
->
audio_duration_queue
);
}
static
int
thread_start
(
Demuxer
*
d
)
{
int
ret
;
InputFile
*
f
=
&
d
->
f
;
if
(
d
->
thread_queue_size
<=
0
)
d
->
thread_queue_size
=
(
nb_input_files
>
1
?
8
:
1
);
if
(
nb_input_files
>
1
&&
(
f
->
ctx
->
pb
?
!
f
->
ctx
->
pb
->
seekable
:
strcmp
(
f
->
ctx
->
iformat
->
name
,
"lavfi"
)))
d
->
non_blocking
=
1
;
ret
=
av_thread_message_queue_alloc
(
&
d
->
in_thread_queue
,
d
->
thread_queue_size
,
sizeof
(
DemuxMsg
));
if
(
ret
<
0
)
return
ret
;
if
(
d
->
loop
)
{
int
nb_audio_dec
=
0
;
for
(
int
i
=
0
;
i
<
f
->
nb_streams
;
i
++
)
{
InputStream
*
ist
=
f
->
streams
[
i
];
nb_audio_dec
+=
!!
(
ist
->
decoding_needed
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_AUDIO
);
}
if
(
nb_audio_dec
)
{
ret
=
av_thread_message_queue_alloc
(
&
f
->
audio_duration_queue
,
nb_audio_dec
,
sizeof
(
LastFrameDuration
));
if
(
ret
<
0
)
goto
fail
;
f
->
audio_duration_queue_size
=
nb_audio_dec
;
}
}
if
((
ret
=
pthread_create
(
&
d
->
thread
,
NULL
,
input_thread
,
d
)))
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"pthread_create failed: %s. Try to increase `ulimit -v` or decrease `ulimit -s`.
\n
"
,
strerror
(
ret
));
ret
=
AVERROR
(
ret
);
goto
fail
;
}
return
0
;
fail:
av_thread_message_queue_free
(
&
d
->
in_thread_queue
);
return
ret
;
}
int
ifile_get_packet
(
InputFile
*
f
,
AVPacket
**
pkt
)
{
Demuxer
*
d
=
demuxer_from_ifile
(
f
);
InputStream
*
ist
;
DemuxMsg
msg
;
int
ret
;
if
(
!
d
->
in_thread_queue
)
{
ret
=
thread_start
(
d
);
if
(
ret
<
0
)
return
ret
;
}
if
(
f
->
readrate
||
f
->
rate_emu
)
{
int
i
;
int64_t
file_start
=
copy_ts
*
(
(
f
->
start_time_effective
!=
AV_NOPTS_VALUE
?
f
->
start_time_effective
*
!
start_at_zero
:
0
)
+
(
f
->
start_time
!=
AV_NOPTS_VALUE
?
f
->
start_time
:
0
)
);
float
scale
=
f
->
rate_emu
?
1
.
0
:
f
->
readrate
;
for
(
i
=
0
;
i
<
f
->
nb_streams
;
i
++
)
{
InputStream
*
ist
=
f
->
streams
[
i
];
int64_t
stream_ts_offset
,
pts
,
now
;
if
(
!
ist
->
nb_packets
||
(
ist
->
decoding_needed
&&
!
ist
->
got_output
))
continue
;
stream_ts_offset
=
FFMAX
(
ist
->
first_dts
!=
AV_NOPTS_VALUE
?
ist
->
first_dts
:
0
,
file_start
);
pts
=
av_rescale
(
ist
->
dts
,
1000000
,
AV_TIME_BASE
);
now
=
(
av_gettime_relative
()
-
ist
->
start
)
*
scale
+
stream_ts_offset
;
if
(
pts
>
now
)
return
AVERROR
(
EAGAIN
);
}
}
ret
=
av_thread_message_queue_recv
(
d
->
in_thread_queue
,
&
msg
,
d
->
non_blocking
?
AV_THREAD_MESSAGE_NONBLOCK
:
0
);
if
(
ret
<
0
)
return
ret
;
if
(
msg
.
looping
)
return
1
;
ist
=
f
->
streams
[
msg
.
pkt
->
stream_index
];
ist
->
last_pkt_repeat_pict
=
msg
.
repeat_pict
;
*
pkt
=
msg
.
pkt
;
return
0
;
}
static
void
ist_free
(
InputStream
**
pist
)
{
InputStream
*
ist
=
*
pist
;
if
(
!
ist
)
return
;
av_frame_free
(
&
ist
->
decoded_frame
);
av_packet_free
(
&
ist
->
pkt
);
av_dict_free
(
&
ist
->
decoder_opts
);
avsubtitle_free
(
&
ist
->
prev_sub
.
subtitle
);
av_frame_free
(
&
ist
->
sub2video
.
frame
);
av_freep
(
&
ist
->
filters
);
av_freep
(
&
ist
->
hwaccel_device
);
av_freep
(
&
ist
->
dts_buffer
);
avcodec_free_context
(
&
ist
->
dec_ctx
);
avcodec_parameters_free
(
&
ist
->
par
);
av_freep
(
pist
);
}
void
ifile_close
(
InputFile
**
pf
)
{
InputFile
*
f
=
*
pf
;
Demuxer
*
d
=
demuxer_from_ifile
(
f
);
if
(
!
f
)
return
;
thread_stop
(
d
);
for
(
int
i
=
0
;
i
<
f
->
nb_streams
;
i
++
)
ist_free
(
&
f
->
streams
[
i
]);
av_freep
(
&
f
->
streams
);
avformat_close_input
(
&
f
->
ctx
);
av_freep
(
pf
);
}
static
const
AVCodec
*
choose_decoder
(
const
OptionsContext
*
o
,
AVFormatContext
*
s
,
AVStream
*
st
,
enum
HWAccelID
hwaccel_id
,
enum
AVHWDeviceType
hwaccel_device_type
)
{
char
*
codec_name
=
NULL
;
MATCH_PER_STREAM_OPT
(
codec_names
,
str
,
codec_name
,
s
,
st
);
if
(
codec_name
)
{
const
AVCodec
*
codec
=
find_codec_or_die
(
NULL
,
codec_name
,
st
->
codecpar
->
codec_type
,
0
);
st
->
codecpar
->
codec_id
=
codec
->
id
;
if
(
recast_media
&&
st
->
codecpar
->
codec_type
!=
codec
->
type
)
st
->
codecpar
->
codec_type
=
codec
->
type
;
return
codec
;
}
else
{
if
(
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
&&
hwaccel_id
==
HWACCEL_GENERIC
&&
hwaccel_device_type
!=
AV_HWDEVICE_TYPE_NONE
)
{
const
AVCodec
*
c
;
void
*
i
=
NULL
;
while
((
c
=
av_codec_iterate
(
&
i
)))
{
const
AVCodecHWConfig
*
config
;
if
(
c
->
id
!=
st
->
codecpar
->
codec_id
||
!
av_codec_is_decoder
(
c
))
continue
;
for
(
int
j
=
0
;
(
config
=
avcodec_get_hw_config
(
c
,
j
));
j
++
)
{
if
(
config
->
device_type
==
hwaccel_device_type
)
{
av_log
(
NULL
,
AV_LOG_VERBOSE
,
"Selecting decoder '%s' because of requested hwaccel method %s
\n
"
,
c
->
name
,
av_hwdevice_get_type_name
(
hwaccel_device_type
));
return
c
;
}
}
}
}
return
avcodec_find_decoder
(
st
->
codecpar
->
codec_id
);
}
}
static
int
guess_input_channel_layout
(
InputStream
*
ist
)
{
AVCodecContext
*
dec
=
ist
->
dec_ctx
;
if
(
dec
->
ch_layout
.
order
==
AV_CHANNEL_ORDER_UNSPEC
)
{
char
layout_name
[
256
];
if
(
dec
->
ch_layout
.
nb_channels
>
ist
->
guess_layout_max
)
return
0
;
av_channel_layout_default
(
&
dec
->
ch_layout
,
dec
->
ch_layout
.
nb_channels
);
if
(
dec
->
ch_layout
.
order
==
AV_CHANNEL_ORDER_UNSPEC
)
return
0
;
av_channel_layout_describe
(
&
dec
->
ch_layout
,
layout_name
,
sizeof
(
layout_name
));
av_log
(
NULL
,
AV_LOG_WARNING
,
"Guessed Channel Layout for Input Stream "
"#%d.%d : %s
\n
"
,
ist
->
file_index
,
ist
->
st
->
index
,
layout_name
);
}
return
1
;
}
static
void
add_display_matrix_to_stream
(
const
OptionsContext
*
o
,
AVFormatContext
*
ctx
,
AVStream
*
st
)
{
double
rotation
=
DBL_MAX
;
int
hflip
=
-
1
,
vflip
=
-
1
;
int
hflip_set
=
0
,
vflip_set
=
0
,
rotation_set
=
0
;
int32_t
*
buf
;
MATCH_PER_STREAM_OPT
(
display_rotations
,
dbl
,
rotation
,
ctx
,
st
);
MATCH_PER_STREAM_OPT
(
display_hflips
,
i
,
hflip
,
ctx
,
st
);
MATCH_PER_STREAM_OPT
(
display_vflips
,
i
,
vflip
,
ctx
,
st
);
rotation_set
=
rotation
!=
DBL_MAX
;
hflip_set
=
hflip
!=
-
1
;
vflip_set
=
vflip
!=
-
1
;
if
(
!
rotation_set
&&
!
hflip_set
&&
!
vflip_set
)
return
;
buf
=
(
int32_t
*
)
av_stream_new_side_data
(
st
,
AV_PKT_DATA_DISPLAYMATRIX
,
sizeof
(
int32_t
)
*
9
);
if
(
!
buf
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"Failed to generate a display matrix!
\n
"
);
exit_program
(
1
);
}
av_display_rotation_set
(
buf
,
rotation_set
?
-
(
rotation
)
:
-
0
.
0
f
);
av_display_matrix_flip
(
buf
,
hflip_set
?
hflip
:
0
,
vflip_set
?
vflip
:
0
);
}
/* Add all the streams from the given input file to the demuxer */
static
void
add_input_streams
(
const
OptionsContext
*
o
,
Demuxer
*
d
)
{
InputFile
*
f
=
&
d
->
f
;
AVFormatContext
*
ic
=
f
->
ctx
;
int
i
,
ret
;
for
(
i
=
0
;
i
<
ic
->
nb_streams
;
i
++
)
{
AVStream
*
st
=
ic
->
streams
[
i
];
AVCodecParameters
*
par
=
st
->
codecpar
;
InputStream
*
ist
;
char
*
framerate
=
NULL
,
*
hwaccel_device
=
NULL
;
const
char
*
hwaccel
=
NULL
;
char
*
hwaccel_output_format
=
NULL
;
char
*
codec_tag
=
NULL
;
char
*
next
;
char
*
discard_str
=
NULL
;
const
AVClass
*
cc
=
avcodec_get_class
();
const
AVOption
*
discard_opt
=
av_opt_find
(
&
cc
,
"skip_frame"
,
NULL
,
0
,
AV_OPT_SEARCH_FAKE_OBJ
);
ist
=
ALLOC_ARRAY_ELEM
(
f
->
streams
,
f
->
nb_streams
);
ist
->
st
=
st
;
ist
->
file_index
=
f
->
index
;
ist
->
discard
=
1
;
st
->
discard
=
AVDISCARD_ALL
;
ist
->
nb_samples
=
0
;
ist
->
first_dts
=
AV_NOPTS_VALUE
;
ist
->
min_pts
=
INT64_MAX
;
ist
->
max_pts
=
INT64_MIN
;
ist
->
ts_scale
=
1
.
0
;
MATCH_PER_STREAM_OPT
(
ts_scale
,
dbl
,
ist
->
ts_scale
,
ic
,
st
);
ist
->
autorotate
=
1
;
MATCH_PER_STREAM_OPT
(
autorotate
,
i
,
ist
->
autorotate
,
ic
,
st
);
MATCH_PER_STREAM_OPT
(
codec_tags
,
str
,
codec_tag
,
ic
,
st
);
if
(
codec_tag
)
{
uint32_t
tag
=
strtol
(
codec_tag
,
&
next
,
0
);
if
(
*
next
)
tag
=
AV_RL32
(
codec_tag
);
st
->
codecpar
->
codec_tag
=
tag
;
}
if
(
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
)
{
add_display_matrix_to_stream
(
o
,
ic
,
st
);
MATCH_PER_STREAM_OPT
(
hwaccels
,
str
,
hwaccel
,
ic
,
st
);
MATCH_PER_STREAM_OPT
(
hwaccel_output_formats
,
str
,
hwaccel_output_format
,
ic
,
st
);
if
(
!
hwaccel_output_format
&&
hwaccel
&&
!
strcmp
(
hwaccel
,
"cuvid"
))
{
av_log
(
NULL
,
AV_LOG_WARNING
,
"WARNING: defaulting hwaccel_output_format to cuda for compatibility "
"with old commandlines. This behaviour is DEPRECATED and will be removed "
"in the future. Please explicitly set
\"
-hwaccel_output_format cuda
\"
.
\n
"
);
ist
->
hwaccel_output_format
=
AV_PIX_FMT_CUDA
;
}
else
if
(
!
hwaccel_output_format
&&
hwaccel
&&
!
strcmp
(
hwaccel
,
"qsv"
))
{
av_log
(
NULL
,
AV_LOG_WARNING
,
"WARNING: defaulting hwaccel_output_format to qsv for compatibility "
"with old commandlines. This behaviour is DEPRECATED and will be removed "
"in the future. Please explicitly set
\"
-hwaccel_output_format qsv
\"
.
\n
"
);
ist
->
hwaccel_output_format
=
AV_PIX_FMT_QSV
;
}
else
if
(
!
hwaccel_output_format
&&
hwaccel
&&
!
strcmp
(
hwaccel
,
"mediacodec"
))
{
// There is no real AVHWFrameContext implementation. Set
// hwaccel_output_format to avoid av_hwframe_transfer_data error.
ist
->
hwaccel_output_format
=
AV_PIX_FMT_MEDIACODEC
;
}
else
if
(
hwaccel_output_format
)
{
ist
->
hwaccel_output_format
=
av_get_pix_fmt
(
hwaccel_output_format
);
if
(
ist
->
hwaccel_output_format
==
AV_PIX_FMT_NONE
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"Unrecognised hwaccel output "
"format: %s"
,
hwaccel_output_format
);
}
}
else
{
ist
->
hwaccel_output_format
=
AV_PIX_FMT_NONE
;
}
if
(
hwaccel
)
{
// The NVDEC hwaccels use a CUDA device, so remap the name here.
if
(
!
strcmp
(
hwaccel
,
"nvdec"
)
||
!
strcmp
(
hwaccel
,
"cuvid"
))
hwaccel
=
"cuda"
;
if
(
!
strcmp
(
hwaccel
,
"none"
))
ist
->
hwaccel_id
=
HWACCEL_NONE
;
else
if
(
!
strcmp
(
hwaccel
,
"auto"
))
ist
->
hwaccel_id
=
HWACCEL_AUTO
;
else
{
enum
AVHWDeviceType
type
=
av_hwdevice_find_type_by_name
(
hwaccel
);
if
(
type
!=
AV_HWDEVICE_TYPE_NONE
)
{
ist
->
hwaccel_id
=
HWACCEL_GENERIC
;
ist
->
hwaccel_device_type
=
type
;
}
if
(
!
ist
->
hwaccel_id
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"Unrecognized hwaccel: %s.
\n
"
,
hwaccel
);
av_log
(
NULL
,
AV_LOG_FATAL
,
"Supported hwaccels: "
);
type
=
AV_HWDEVICE_TYPE_NONE
;
while
((
type
=
av_hwdevice_iterate_types
(
type
))
!=
AV_HWDEVICE_TYPE_NONE
)
av_log
(
NULL
,
AV_LOG_FATAL
,
"%s "
,
av_hwdevice_get_type_name
(
type
));
av_log
(
NULL
,
AV_LOG_FATAL
,
"
\n
"
);
exit_program
(
1
);
}
}
}
MATCH_PER_STREAM_OPT
(
hwaccel_devices
,
str
,
hwaccel_device
,
ic
,
st
);
if
(
hwaccel_device
)
{
ist
->
hwaccel_device
=
av_strdup
(
hwaccel_device
);
if
(
!
ist
->
hwaccel_device
)
report_and_exit
(
AVERROR
(
ENOMEM
));
}
ist
->
hwaccel_pix_fmt
=
AV_PIX_FMT_NONE
;
}
ist
->
dec
=
choose_decoder
(
o
,
ic
,
st
,
ist
->
hwaccel_id
,
ist
->
hwaccel_device_type
);
ist
->
decoder_opts
=
filter_codec_opts
(
o
->
g
->
codec_opts
,
ist
->
st
->
codecpar
->
codec_id
,
ic
,
st
,
ist
->
dec
);
ist
->
reinit_filters
=
-
1
;
MATCH_PER_STREAM_OPT
(
reinit_filters
,
i
,
ist
->
reinit_filters
,
ic
,
st
);
MATCH_PER_STREAM_OPT
(
discard
,
str
,
discard_str
,
ic
,
st
);
ist
->
user_set_discard
=
AVDISCARD_NONE
;
if
((
o
->
video_disable
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
)
||
(
o
->
audio_disable
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_AUDIO
)
||
(
o
->
subtitle_disable
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_SUBTITLE
)
||
(
o
->
data_disable
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_DATA
))
ist
->
user_set_discard
=
AVDISCARD_ALL
;
if
(
discard_str
&&
av_opt_eval_int
(
&
cc
,
discard_opt
,
discard_str
,
&
ist
->
user_set_discard
)
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error parsing discard %s.
\n
"
,
discard_str
);
exit_program
(
1
);
}
ist
->
filter_in_rescale_delta_last
=
AV_NOPTS_VALUE
;
ist
->
prev_pkt_pts
=
AV_NOPTS_VALUE
;
ist
->
dec_ctx
=
avcodec_alloc_context3
(
ist
->
dec
);
if
(
!
ist
->
dec_ctx
)
report_and_exit
(
AVERROR
(
ENOMEM
));
ret
=
avcodec_parameters_to_context
(
ist
->
dec_ctx
,
par
);
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error initializing the decoder context.
\n
"
);
exit_program
(
1
);
}
ist
->
decoded_frame
=
av_frame_alloc
();
if
(
!
ist
->
decoded_frame
)
report_and_exit
(
AVERROR
(
ENOMEM
));
ist
->
pkt
=
av_packet_alloc
();
if
(
!
ist
->
pkt
)
report_and_exit
(
AVERROR
(
ENOMEM
));
if
(
o
->
bitexact
)
ist
->
dec_ctx
->
flags
|=
AV_CODEC_FLAG_BITEXACT
;
switch
(
par
->
codec_type
)
{
case
AVMEDIA_TYPE_VIDEO
:
// avformat_find_stream_info() doesn't set this for us anymore.
ist
->
dec_ctx
->
framerate
=
st
->
avg_frame_rate
;
MATCH_PER_STREAM_OPT
(
frame_rates
,
str
,
framerate
,
ic
,
st
);
if
(
framerate
&&
av_parse_video_rate
(
&
ist
->
framerate
,
framerate
)
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error parsing framerate %s.
\n
"
,
framerate
);
exit_program
(
1
);
}
ist
->
top_field_first
=
-
1
;
MATCH_PER_STREAM_OPT
(
top_field_first
,
i
,
ist
->
top_field_first
,
ic
,
st
);
ist
->
framerate_guessed
=
av_guess_frame_rate
(
ic
,
st
,
NULL
);
break
;
case
AVMEDIA_TYPE_AUDIO
:
ist
->
guess_layout_max
=
INT_MAX
;
MATCH_PER_STREAM_OPT
(
guess_layout_max
,
i
,
ist
->
guess_layout_max
,
ic
,
st
);
guess_input_channel_layout
(
ist
);
break
;
case
AVMEDIA_TYPE_DATA
:
case
AVMEDIA_TYPE_SUBTITLE
:
{
char
*
canvas_size
=
NULL
;
MATCH_PER_STREAM_OPT
(
fix_sub_duration
,
i
,
ist
->
fix_sub_duration
,
ic
,
st
);
MATCH_PER_STREAM_OPT
(
canvas_sizes
,
str
,
canvas_size
,
ic
,
st
);
if
(
canvas_size
&&
av_parse_video_size
(
&
ist
->
dec_ctx
->
width
,
&
ist
->
dec_ctx
->
height
,
canvas_size
)
<
0
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"Invalid canvas size: %s.
\n
"
,
canvas_size
);
exit_program
(
1
);
}
break
;
}
case
AVMEDIA_TYPE_ATTACHMENT
:
case
AVMEDIA_TYPE_UNKNOWN
:
break
;
default:
abort
();
}
ist
->
par
=
avcodec_parameters_alloc
();
if
(
!
ist
->
par
)
report_and_exit
(
AVERROR
(
ENOMEM
));
ret
=
avcodec_parameters_from_context
(
ist
->
par
,
ist
->
dec_ctx
);
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error initializing the decoder context.
\n
"
);
exit_program
(
1
);
}
}
}
static
void
dump_attachment
(
AVStream
*
st
,
const
char
*
filename
)
{
int
ret
;
AVIOContext
*
out
=
NULL
;
const
AVDictionaryEntry
*
e
;
if
(
!
st
->
codecpar
->
extradata_size
)
{
av_log
(
NULL
,
AV_LOG_WARNING
,
"No extradata to dump in stream #%d:%d.
\n
"
,
nb_input_files
-
1
,
st
->
index
);
return
;
}
if
(
!*
filename
&&
(
e
=
av_dict_get
(
st
->
metadata
,
"filename"
,
NULL
,
0
)))
filename
=
e
->
value
;
if
(
!*
filename
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"No filename specified and no 'filename' tag"
"in stream #%d:%d.
\n
"
,
nb_input_files
-
1
,
st
->
index
);
exit_program
(
1
);
}
assert_file_overwrite
(
filename
);
if
((
ret
=
avio_open2
(
&
out
,
filename
,
AVIO_FLAG_WRITE
,
&
int_cb
,
NULL
))
<
0
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"Could not open file %s for writing.
\n
"
,
filename
);
exit_program
(
1
);
}
avio_write
(
out
,
st
->
codecpar
->
extradata
,
st
->
codecpar
->
extradata_size
);
avio_flush
(
out
);
avio_close
(
out
);
}
int
ifile_open
(
const
OptionsContext
*
o
,
const
char
*
filename
)
{
Demuxer
*
d
;
InputFile
*
f
;
AVFormatContext
*
ic
;
const
AVInputFormat
*
file_iformat
=
NULL
;
int
err
,
i
,
ret
;
int64_t
timestamp
;
AVDictionary
*
unused_opts
=
NULL
;
const
AVDictionaryEntry
*
e
=
NULL
;
char
*
video_codec_name
=
NULL
;
char
*
audio_codec_name
=
NULL
;
char
*
subtitle_codec_name
=
NULL
;
char
*
data_codec_name
=
NULL
;
int
scan_all_pmts_set
=
0
;
int64_t
start_time
=
o
->
start_time
;
int64_t
start_time_eof
=
o
->
start_time_eof
;
int64_t
stop_time
=
o
->
stop_time
;
int64_t
recording_time
=
o
->
recording_time
;
if
(
stop_time
!=
INT64_MAX
&&
recording_time
!=
INT64_MAX
)
{
stop_time
=
INT64_MAX
;
av_log
(
NULL
,
AV_LOG_WARNING
,
"-t and -to cannot be used together; using -t.
\n
"
);
}
if
(
stop_time
!=
INT64_MAX
&&
recording_time
==
INT64_MAX
)
{
int64_t
start
=
start_time
==
AV_NOPTS_VALUE
?
0
:
start_time
;
if
(
stop_time
<=
start
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"-to value smaller than -ss; aborting.
\n
"
);
exit_program
(
1
);
}
else
{
recording_time
=
stop_time
-
start
;
}
}
if
(
o
->
format
)
{
if
(
!
(
file_iformat
=
av_find_input_format
(
o
->
format
)))
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"Unknown input format: '%s'
\n
"
,
o
->
format
);
exit_program
(
1
);
}
}
if
(
!
strcmp
(
filename
,
"-"
))
filename
=
"fd:"
;
stdin_interaction
&=
strncmp
(
filename
,
"pipe:"
,
5
)
&&
strcmp
(
filename
,
"fd:"
)
&&
strcmp
(
filename
,
"/dev/stdin"
);
/* get default parameters from command line */
ic
=
avformat_alloc_context
();
if
(
!
ic
)
report_and_exit
(
AVERROR
(
ENOMEM
));
if
(
o
->
nb_audio_sample_rate
)
{
av_dict_set_int
(
&
o
->
g
->
format_opts
,
"sample_rate"
,
o
->
audio_sample_rate
[
o
->
nb_audio_sample_rate
-
1
].
u
.
i
,
0
);
}
if
(
o
->
nb_audio_channels
)
{
const
AVClass
*
priv_class
;
if
(
file_iformat
&&
(
priv_class
=
file_iformat
->
priv_class
)
&&
av_opt_find
(
&
priv_class
,
"ch_layout"
,
NULL
,
0
,
AV_OPT_SEARCH_FAKE_OBJ
))
{
char
buf
[
32
];
snprintf
(
buf
,
sizeof
(
buf
),
"%dC"
,
o
->
audio_channels
[
o
->
nb_audio_channels
-
1
].
u
.
i
);
av_dict_set
(
&
o
->
g
->
format_opts
,
"ch_layout"
,
buf
,
0
);
}
}
if
(
o
->
nb_audio_ch_layouts
)
{
const
AVClass
*
priv_class
;
if
(
file_iformat
&&
(
priv_class
=
file_iformat
->
priv_class
)
&&
av_opt_find
(
&
priv_class
,
"ch_layout"
,
NULL
,
0
,
AV_OPT_SEARCH_FAKE_OBJ
))
{
av_dict_set
(
&
o
->
g
->
format_opts
,
"ch_layout"
,
o
->
audio_ch_layouts
[
o
->
nb_audio_ch_layouts
-
1
].
u
.
str
,
0
);
}
}
if
(
o
->
nb_frame_rates
)
{
const
AVClass
*
priv_class
;
/* set the format-level framerate option;
* this is important for video grabbers, e.g. x11 */
if
(
file_iformat
&&
(
priv_class
=
file_iformat
->
priv_class
)
&&
av_opt_find
(
&
priv_class
,
"framerate"
,
NULL
,
0
,
AV_OPT_SEARCH_FAKE_OBJ
))
{
av_dict_set
(
&
o
->
g
->
format_opts
,
"framerate"
,
o
->
frame_rates
[
o
->
nb_frame_rates
-
1
].
u
.
str
,
0
);
}
}
if
(
o
->
nb_frame_sizes
)
{
av_dict_set
(
&
o
->
g
->
format_opts
,
"video_size"
,
o
->
frame_sizes
[
o
->
nb_frame_sizes
-
1
].
u
.
str
,
0
);
}
if
(
o
->
nb_frame_pix_fmts
)
av_dict_set
(
&
o
->
g
->
format_opts
,
"pixel_format"
,
o
->
frame_pix_fmts
[
o
->
nb_frame_pix_fmts
-
1
].
u
.
str
,
0
);
MATCH_PER_TYPE_OPT
(
codec_names
,
str
,
video_codec_name
,
ic
,
"v"
);
MATCH_PER_TYPE_OPT
(
codec_names
,
str
,
audio_codec_name
,
ic
,
"a"
);
MATCH_PER_TYPE_OPT
(
codec_names
,
str
,
subtitle_codec_name
,
ic
,
"s"
);
MATCH_PER_TYPE_OPT
(
codec_names
,
str
,
data_codec_name
,
ic
,
"d"
);
if
(
video_codec_name
)
ic
->
video_codec
=
find_codec_or_die
(
NULL
,
video_codec_name
,
AVMEDIA_TYPE_VIDEO
,
0
);
if
(
audio_codec_name
)
ic
->
audio_codec
=
find_codec_or_die
(
NULL
,
audio_codec_name
,
AVMEDIA_TYPE_AUDIO
,
0
);
if
(
subtitle_codec_name
)
ic
->
subtitle_codec
=
find_codec_or_die
(
NULL
,
subtitle_codec_name
,
AVMEDIA_TYPE_SUBTITLE
,
0
);
if
(
data_codec_name
)
ic
->
data_codec
=
find_codec_or_die
(
NULL
,
data_codec_name
,
AVMEDIA_TYPE_DATA
,
0
);
ic
->
video_codec_id
=
video_codec_name
?
ic
->
video_codec
->
id
:
AV_CODEC_ID_NONE
;
ic
->
audio_codec_id
=
audio_codec_name
?
ic
->
audio_codec
->
id
:
AV_CODEC_ID_NONE
;
ic
->
subtitle_codec_id
=
subtitle_codec_name
?
ic
->
subtitle_codec
->
id
:
AV_CODEC_ID_NONE
;
ic
->
data_codec_id
=
data_codec_name
?
ic
->
data_codec
->
id
:
AV_CODEC_ID_NONE
;
ic
->
flags
|=
AVFMT_FLAG_NONBLOCK
;
if
(
o
->
bitexact
)
ic
->
flags
|=
AVFMT_FLAG_BITEXACT
;
ic
->
interrupt_callback
=
int_cb
;
if
(
!
av_dict_get
(
o
->
g
->
format_opts
,
"scan_all_pmts"
,
NULL
,
AV_DICT_MATCH_CASE
))
{
av_dict_set
(
&
o
->
g
->
format_opts
,
"scan_all_pmts"
,
"1"
,
AV_DICT_DONT_OVERWRITE
);
scan_all_pmts_set
=
1
;
}
/* open the input file with generic avformat function */
err
=
avformat_open_input
(
&
ic
,
filename
,
file_iformat
,
&
o
->
g
->
format_opts
);
if
(
err
<
0
)
{
print_error
(
filename
,
err
);
if
(
err
==
AVERROR_PROTOCOL_NOT_FOUND
)
av_log
(
NULL
,
AV_LOG_ERROR
,
"Did you mean file:%s?
\n
"
,
filename
);
exit_program
(
1
);
}
if
(
scan_all_pmts_set
)
av_dict_set
(
&
o
->
g
->
format_opts
,
"scan_all_pmts"
,
NULL
,
AV_DICT_MATCH_CASE
);
remove_avoptions
(
&
o
->
g
->
format_opts
,
o
->
g
->
codec_opts
);
assert_avoptions
(
o
->
g
->
format_opts
);
/* apply forced codec ids */
for
(
i
=
0
;
i
<
ic
->
nb_streams
;
i
++
)
choose_decoder
(
o
,
ic
,
ic
->
streams
[
i
],
HWACCEL_NONE
,
AV_HWDEVICE_TYPE_NONE
);
if
(
o
->
find_stream_info
)
{
AVDictionary
**
opts
=
setup_find_stream_info_opts
(
ic
,
o
->
g
->
codec_opts
);
int
orig_nb_streams
=
ic
->
nb_streams
;
/* If not enough info to get the stream parameters, we decode the
first frames to get it. (used in mpeg case for example) */
ret
=
avformat_find_stream_info
(
ic
,
opts
);
for
(
i
=
0
;
i
<
orig_nb_streams
;
i
++
)
av_dict_free
(
&
opts
[
i
]);
av_freep
(
&
opts
);
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"%s: could not find codec parameters
\n
"
,
filename
);
if
(
ic
->
nb_streams
==
0
)
{
avformat_close_input
(
&
ic
);
exit_program
(
1
);
}
}
}
if
(
start_time
!=
AV_NOPTS_VALUE
&&
start_time_eof
!=
AV_NOPTS_VALUE
)
{
av_log
(
NULL
,
AV_LOG_WARNING
,
"Cannot use -ss and -sseof both, using -ss for %s
\n
"
,
filename
);
start_time_eof
=
AV_NOPTS_VALUE
;
}
if
(
start_time_eof
!=
AV_NOPTS_VALUE
)
{
if
(
start_time_eof
>=
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"-sseof value must be negative; aborting
\n
"
);
exit_program
(
1
);
}
if
(
ic
->
duration
>
0
)
{
start_time
=
start_time_eof
+
ic
->
duration
;
if
(
start_time
<
0
)
{
av_log
(
NULL
,
AV_LOG_WARNING
,
"-sseof value seeks to before start of file %s; ignored
\n
"
,
filename
);
start_time
=
AV_NOPTS_VALUE
;
}
}
else
av_log
(
NULL
,
AV_LOG_WARNING
,
"Cannot use -sseof, duration of %s not known
\n
"
,
filename
);
}
timestamp
=
(
start_time
==
AV_NOPTS_VALUE
)
?
0
:
start_time
;
/* add the stream start time */
if
(
!
o
->
seek_timestamp
&&
ic
->
start_time
!=
AV_NOPTS_VALUE
)
timestamp
+=
ic
->
start_time
;
/* if seeking requested, we execute it */
if
(
start_time
!=
AV_NOPTS_VALUE
)
{
int64_t
seek_timestamp
=
timestamp
;
if
(
!
(
ic
->
iformat
->
flags
&
AVFMT_SEEK_TO_PTS
))
{
int
dts_heuristic
=
0
;
for
(
i
=
0
;
i
<
ic
->
nb_streams
;
i
++
)
{
const
AVCodecParameters
*
par
=
ic
->
streams
[
i
]
->
codecpar
;
if
(
par
->
video_delay
)
{
dts_heuristic
=
1
;
break
;
}
}
if
(
dts_heuristic
)
{
seek_timestamp
-=
3
*
AV_TIME_BASE
/
23
;
}
}
ret
=
avformat_seek_file
(
ic
,
-
1
,
INT64_MIN
,
seek_timestamp
,
seek_timestamp
,
0
);
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_WARNING
,
"%s: could not seek to position %0.3f
\n
"
,
filename
,
(
double
)
timestamp
/
AV_TIME_BASE
);
}
}
d
=
allocate_array_elem
(
&
input_files
,
sizeof
(
*
d
),
&
nb_input_files
);
f
=
&
d
->
f
;
f
->
ctx
=
ic
;
f
->
index
=
nb_input_files
-
1
;
f
->
start_time
=
start_time
;
f
->
recording_time
=
recording_time
;
f
->
input_sync_ref
=
o
->
input_sync_ref
;
f
->
input_ts_offset
=
o
->
input_ts_offset
;
f
->
ts_offset
=
o
->
input_ts_offset
-
(
copy_ts
?
(
start_at_zero
&&
ic
->
start_time
!=
AV_NOPTS_VALUE
?
ic
->
start_time
:
0
)
:
timestamp
);
f
->
rate_emu
=
o
->
rate_emu
;
f
->
accurate_seek
=
o
->
accurate_seek
;
d
->
loop
=
o
->
loop
;
d
->
duration
=
0
;
d
->
time_base
=
(
AVRational
){
1
,
1
};
f
->
readrate
=
o
->
readrate
?
o
->
readrate
:
0
.
0
;
if
(
f
->
readrate
<
0
.
0
f
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Option -readrate for Input #%d is %0.3f; it must be non-negative.
\n
"
,
f
->
index
,
f
->
readrate
);
exit_program
(
1
);
}
if
(
f
->
readrate
&&
f
->
rate_emu
)
{
av_log
(
NULL
,
AV_LOG_WARNING
,
"Both -readrate and -re set for Input #%d. Using -readrate %0.3f.
\n
"
,
f
->
index
,
f
->
readrate
);
f
->
rate_emu
=
0
;
}
d
->
thread_queue_size
=
o
->
thread_queue_size
;
/* update the current parameters so that they match the one of the input stream */
add_input_streams
(
o
,
d
);
/* dump the file content */
av_dump_format
(
ic
,
f
->
index
,
filename
,
0
);
/* check if all codec options have been used */
unused_opts
=
strip_specifiers
(
o
->
g
->
codec_opts
);
for
(
i
=
0
;
i
<
f
->
nb_streams
;
i
++
)
{
e
=
NULL
;
while
((
e
=
av_dict_iterate
(
f
->
streams
[
i
]
->
decoder_opts
,
e
)))
av_dict_set
(
&
unused_opts
,
e
->
key
,
NULL
,
0
);
}
e
=
NULL
;
while
((
e
=
av_dict_iterate
(
unused_opts
,
e
)))
{
const
AVClass
*
class
=
avcodec_get_class
();
const
AVOption
*
option
=
av_opt_find
(
&
class
,
e
->
key
,
NULL
,
0
,
AV_OPT_SEARCH_CHILDREN
|
AV_OPT_SEARCH_FAKE_OBJ
);
const
AVClass
*
fclass
=
avformat_get_class
();
const
AVOption
*
foption
=
av_opt_find
(
&
fclass
,
e
->
key
,
NULL
,
0
,
AV_OPT_SEARCH_CHILDREN
|
AV_OPT_SEARCH_FAKE_OBJ
);
if
(
!
option
||
foption
)
continue
;
if
(
!
(
option
->
flags
&
AV_OPT_FLAG_DECODING_PARAM
))
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Codec AVOption %s (%s) specified for "
"input file #%d (%s) is not a decoding option.
\n
"
,
e
->
key
,
option
->
help
?
option
->
help
:
""
,
f
->
index
,
filename
);
exit_program
(
1
);
}
av_log
(
NULL
,
AV_LOG_WARNING
,
"Codec AVOption %s (%s) specified for "
"input file #%d (%s) has not been used for any stream. The most "
"likely reason is either wrong type (e.g. a video option with "
"no video streams) or that it is a private option of some decoder "
"which was not actually used for any stream.
\n
"
,
e
->
key
,
option
->
help
?
option
->
help
:
""
,
f
->
index
,
filename
);
}
av_dict_free
(
&
unused_opts
);
for
(
i
=
0
;
i
<
o
->
nb_dump_attachment
;
i
++
)
{
int
j
;
for
(
j
=
0
;
j
<
ic
->
nb_streams
;
j
++
)
{
AVStream
*
st
=
ic
->
streams
[
j
];
if
(
check_stream_specifier
(
ic
,
st
,
o
->
dump_attachment
[
i
].
specifier
)
==
1
)
dump_attachment
(
st
,
o
->
dump_attachment
[
i
].
u
.
str
);
}
}
return
0
;
}
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_filter.c
浏览文件 @
f23762b9
/*
/*
* ffmpeg filter configuration
* ffmpeg filter configuration
* copyright (c) 2018 Taner Sener ( tanersener gmail com )
* Copyright (c) 2018 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
*
*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
*
*
...
@@ -24,6 +25,12 @@
...
@@ -24,6 +25,12 @@
* We manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* We manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
*
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
*
*
* 08.2018
* 08.2018
...
@@ -69,8 +76,9 @@ static const enum AVPixelFormat *get_compliance_normal_pix_fmts(const AVCodec *c
...
@@ -69,8 +76,9 @@ static const enum AVPixelFormat *get_compliance_normal_pix_fmts(const AVCodec *c
}
}
}
}
enum
AVPixelFormat
choose_pixel_fmt
(
AVStream
*
st
,
AVCodecContext
*
enc_ctx
,
enum
AVPixelFormat
const
AVCodec
*
codec
,
enum
AVPixelFormat
target
)
choose_pixel_fmt
(
const
AVCodec
*
codec
,
enum
AVPixelFormat
target
,
int
strict_std_compliance
)
{
{
if
(
codec
&&
codec
->
pix_fmts
)
{
if
(
codec
&&
codec
->
pix_fmts
)
{
const
enum
AVPixelFormat
*
p
=
codec
->
pix_fmts
;
const
enum
AVPixelFormat
*
p
=
codec
->
pix_fmts
;
...
@@ -79,7 +87,7 @@ enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *enc_ctx,
...
@@ -79,7 +87,7 @@ enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *enc_ctx,
int
has_alpha
=
desc
?
desc
->
nb_components
%
2
==
0
:
0
;
int
has_alpha
=
desc
?
desc
->
nb_components
%
2
==
0
:
0
;
enum
AVPixelFormat
best
=
AV_PIX_FMT_NONE
;
enum
AVPixelFormat
best
=
AV_PIX_FMT_NONE
;
if
(
enc_ctx
->
strict_std_compliance
>
FF_COMPLIANCE_UNOFFICIAL
)
{
if
(
strict_std_compliance
>
FF_COMPLIANCE_UNOFFICIAL
)
{
p
=
get_compliance_normal_pix_fmts
(
codec
,
p
);
p
=
get_compliance_normal_pix_fmts
(
codec
,
p
);
}
}
for
(;
*
p
!=
AV_PIX_FMT_NONE
;
p
++
)
{
for
(;
*
p
!=
AV_PIX_FMT_NONE
;
p
++
)
{
...
@@ -106,6 +114,7 @@ enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *enc_ctx,
...
@@ -106,6 +114,7 @@ enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *enc_ctx,
static
const
char
*
choose_pix_fmts
(
OutputFilter
*
ofilter
,
AVBPrint
*
bprint
)
static
const
char
*
choose_pix_fmts
(
OutputFilter
*
ofilter
,
AVBPrint
*
bprint
)
{
{
OutputStream
*
ost
=
ofilter
->
ost
;
OutputStream
*
ost
=
ofilter
->
ost
;
AVCodecContext
*
enc
=
ost
->
enc_ctx
;
const
AVDictionaryEntry
*
strict_dict
=
av_dict_get
(
ost
->
encoder_opts
,
"strict"
,
NULL
,
0
);
const
AVDictionaryEntry
*
strict_dict
=
av_dict_get
(
ost
->
encoder_opts
,
"strict"
,
NULL
,
0
);
if
(
strict_dict
)
if
(
strict_dict
)
// used by choose_pixel_fmt() and below
// used by choose_pixel_fmt() and below
...
@@ -119,13 +128,14 @@ static const char *choose_pix_fmts(OutputFilter *ofilter, AVBPrint *bprint)
...
@@ -119,13 +128,14 @@ static const char *choose_pix_fmts(OutputFilter *ofilter, AVBPrint *bprint)
return
av_get_pix_fmt_name
(
ost
->
enc_ctx
->
pix_fmt
);
return
av_get_pix_fmt_name
(
ost
->
enc_ctx
->
pix_fmt
);
}
}
if
(
ost
->
enc_ctx
->
pix_fmt
!=
AV_PIX_FMT_NONE
)
{
if
(
ost
->
enc_ctx
->
pix_fmt
!=
AV_PIX_FMT_NONE
)
{
return
av_get_pix_fmt_name
(
choose_pixel_fmt
(
ost
->
st
,
ost
->
enc_ctx
,
ost
->
enc
,
ost
->
enc_ctx
->
pix_fmt
));
return
av_get_pix_fmt_name
(
choose_pixel_fmt
(
enc
->
codec
,
enc
->
pix_fmt
,
}
else
if
(
ost
->
enc
&&
ost
->
enc
->
pix_fmts
)
{
ost
->
enc_ctx
->
strict_std_compliance
));
}
else
if
(
enc
->
codec
->
pix_fmts
)
{
const
enum
AVPixelFormat
*
p
;
const
enum
AVPixelFormat
*
p
;
p
=
ost
->
en
c
->
pix_fmts
;
p
=
enc
->
code
c
->
pix_fmts
;
if
(
ost
->
enc_ctx
->
strict_std_compliance
>
FF_COMPLIANCE_UNOFFICIAL
)
{
if
(
ost
->
enc_ctx
->
strict_std_compliance
>
FF_COMPLIANCE_UNOFFICIAL
)
{
p
=
get_compliance_normal_pix_fmts
(
ost
->
en
c
,
p
);
p
=
get_compliance_normal_pix_fmts
(
enc
->
code
c
,
p
);
}
}
for
(;
*
p
!=
AV_PIX_FMT_NONE
;
p
++
)
{
for
(;
*
p
!=
AV_PIX_FMT_NONE
;
p
++
)
{
...
@@ -133,7 +143,7 @@ static const char *choose_pix_fmts(OutputFilter *ofilter, AVBPrint *bprint)
...
@@ -133,7 +143,7 @@ static const char *choose_pix_fmts(OutputFilter *ofilter, AVBPrint *bprint)
av_bprintf
(
bprint
,
"%s%c"
,
name
,
p
[
1
]
==
AV_PIX_FMT_NONE
?
'\0'
:
'|'
);
av_bprintf
(
bprint
,
"%s%c"
,
name
,
p
[
1
]
==
AV_PIX_FMT_NONE
?
'\0'
:
'|'
);
}
}
if
(
!
av_bprint_is_complete
(
bprint
))
if
(
!
av_bprint_is_complete
(
bprint
))
exit_program
(
1
);
report_and_exit
(
AVERROR
(
ENOMEM
)
);
return
bprint
->
str
;
return
bprint
->
str
;
}
else
}
else
return
NULL
;
return
NULL
;
...
@@ -197,7 +207,7 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost)
...
@@ -197,7 +207,7 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost)
InputFilter
*
ifilter
;
InputFilter
*
ifilter
;
if
(
!
fg
)
if
(
!
fg
)
exit_program
(
1
);
report_and_exit
(
AVERROR
(
ENOMEM
)
);
fg
->
index
=
nb_filtergraphs
;
fg
->
index
=
nb_filtergraphs
;
ofilter
=
ALLOC_ARRAY_ELEM
(
fg
->
outputs
,
fg
->
nb_outputs
);
ofilter
=
ALLOC_ARRAY_ELEM
(
fg
->
outputs
,
fg
->
nb_outputs
);
...
@@ -214,7 +224,7 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost)
...
@@ -214,7 +224,7 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost)
ifilter
->
frame_queue
=
av_fifo_alloc2
(
8
,
sizeof
(
AVFrame
*
),
AV_FIFO_FLAG_AUTO_GROW
);
ifilter
->
frame_queue
=
av_fifo_alloc2
(
8
,
sizeof
(
AVFrame
*
),
AV_FIFO_FLAG_AUTO_GROW
);
if
(
!
ifilter
->
frame_queue
)
if
(
!
ifilter
->
frame_queue
)
exit_program
(
1
);
report_and_exit
(
AVERROR
(
ENOMEM
)
);
GROW_ARRAY
(
ist
->
filters
,
ist
->
nb_filters
);
GROW_ARRAY
(
ist
->
filters
,
ist
->
nb_filters
);
ist
->
filters
[
ist
->
nb_filters
-
1
]
=
ifilter
;
ist
->
filters
[
ist
->
nb_filters
-
1
]
=
ifilter
;
...
@@ -238,7 +248,7 @@ static char *describe_filter_link(FilterGraph *fg, AVFilterInOut *inout, int in)
...
@@ -238,7 +248,7 @@ static char *describe_filter_link(FilterGraph *fg, AVFilterInOut *inout, int in)
res
=
av_asprintf
(
"%s:%s"
,
ctx
->
filter
->
name
,
res
=
av_asprintf
(
"%s:%s"
,
ctx
->
filter
->
name
,
avfilter_pad_get_name
(
pads
,
inout
->
pad_idx
));
avfilter_pad_get_name
(
pads
,
inout
->
pad_idx
));
if
(
!
res
)
if
(
!
res
)
exit_program
(
1
);
report_and_exit
(
AVERROR
(
ENOMEM
)
);
return
res
;
return
res
;
}
}
...
@@ -285,7 +295,7 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
...
@@ -285,7 +295,7 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
"matches no streams.
\n
"
,
p
,
fg
->
graph_desc
);
"matches no streams.
\n
"
,
p
,
fg
->
graph_desc
);
exit_program
(
1
);
exit_program
(
1
);
}
}
ist
=
input_
streams
[
input_files
[
file_idx
]
->
ist_index
+
st
->
index
];
ist
=
input_
files
[
file_idx
]
->
streams
[
st
->
index
];
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
)
{
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"Stream specifier '%s' in filtergraph description %s "
av_log
(
NULL
,
AV_LOG_FATAL
,
"Stream specifier '%s' in filtergraph description %s "
"matches a disabled input stream.
\n
"
,
p
,
fg
->
graph_desc
);
"matches a disabled input stream.
\n
"
,
p
,
fg
->
graph_desc
);
...
@@ -293,14 +303,13 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
...
@@ -293,14 +303,13 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
}
}
}
else
{
}
else
{
/* find the first unused stream of corresponding type */
/* find the first unused stream of corresponding type */
for
(
i
=
0
;
i
<
nb_input_streams
;
i
++
)
{
for
(
ist
=
ist_iter
(
NULL
);
ist
;
ist
=
ist_iter
(
ist
))
{
ist
=
input_streams
[
i
];
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
)
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
)
continue
;
continue
;
if
(
ist
->
dec_ctx
->
codec_type
==
type
&&
ist
->
discard
)
if
(
ist
->
dec_ctx
->
codec_type
==
type
&&
ist
->
discard
)
break
;
break
;
}
}
if
(
i
==
nb_input_streams
)
{
if
(
!
ist
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"Cannot find a matching stream for "
av_log
(
NULL
,
AV_LOG_FATAL
,
"Cannot find a matching stream for "
"unlabeled input pad %d on filter %s
\n
"
,
in
->
pad_idx
,
"unlabeled input pad %d on filter %s
\n
"
,
in
->
pad_idx
,
in
->
filter_ctx
->
name
);
in
->
filter_ctx
->
name
);
...
@@ -323,12 +332,162 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
...
@@ -323,12 +332,162 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
ifilter
->
frame_queue
=
av_fifo_alloc2
(
8
,
sizeof
(
AVFrame
*
),
AV_FIFO_FLAG_AUTO_GROW
);
ifilter
->
frame_queue
=
av_fifo_alloc2
(
8
,
sizeof
(
AVFrame
*
),
AV_FIFO_FLAG_AUTO_GROW
);
if
(
!
ifilter
->
frame_queue
)
if
(
!
ifilter
->
frame_queue
)
exit_program
(
1
);
report_and_exit
(
AVERROR
(
ENOMEM
)
);
GROW_ARRAY
(
ist
->
filters
,
ist
->
nb_filters
);
GROW_ARRAY
(
ist
->
filters
,
ist
->
nb_filters
);
ist
->
filters
[
ist
->
nb_filters
-
1
]
=
ifilter
;
ist
->
filters
[
ist
->
nb_filters
-
1
]
=
ifilter
;
}
}
static
int
read_binary
(
const
char
*
path
,
uint8_t
**
data
,
int
*
len
)
{
AVIOContext
*
io
=
NULL
;
int64_t
fsize
;
int
ret
;
*
data
=
NULL
;
*
len
=
0
;
ret
=
avio_open2
(
&
io
,
path
,
AVIO_FLAG_READ
,
&
int_cb
,
NULL
);
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Cannot open file '%s': %s
\n
"
,
path
,
av_err2str
(
ret
));
return
ret
;
}
fsize
=
avio_size
(
io
);
if
(
fsize
<
0
||
fsize
>
INT_MAX
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Cannot obtain size of file %s
\n
"
,
path
);
ret
=
AVERROR
(
EIO
);
goto
fail
;
}
*
data
=
av_malloc
(
fsize
);
if
(
!*
data
)
{
ret
=
AVERROR
(
ENOMEM
);
goto
fail
;
}
ret
=
avio_read
(
io
,
*
data
,
fsize
);
if
(
ret
!=
fsize
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error reading file %s
\n
"
,
path
);
ret
=
ret
<
0
?
ret
:
AVERROR
(
EIO
);
goto
fail
;
}
*
len
=
fsize
;
return
0
;
fail
:
avio_close
(
io
);
av_freep
(
data
);
*
len
=
0
;
return
ret
;
}
static
int
filter_opt_apply
(
AVFilterContext
*
f
,
const
char
*
key
,
const
char
*
val
)
{
const
AVOption
*
o
=
NULL
;
int
ret
;
ret
=
av_opt_set
(
f
,
key
,
val
,
AV_OPT_SEARCH_CHILDREN
);
if
(
ret
>=
0
)
return
0
;
if
(
ret
==
AVERROR_OPTION_NOT_FOUND
&&
key
[
0
]
==
'/'
)
o
=
av_opt_find
(
f
,
key
+
1
,
NULL
,
0
,
AV_OPT_SEARCH_CHILDREN
);
if
(
!
o
)
goto
err_apply
;
// key is a valid option name prefixed with '/'
// interpret value as a path from which to load the actual option value
key
++
;
if
(
o
->
type
==
AV_OPT_TYPE_BINARY
)
{
uint8_t
*
data
;
int
len
;
ret
=
read_binary
(
val
,
&
data
,
&
len
);
if
(
ret
<
0
)
goto
err_load
;
ret
=
av_opt_set_bin
(
f
,
key
,
data
,
len
,
AV_OPT_SEARCH_CHILDREN
);
av_freep
(
&
data
);
}
else
{
char
*
data
=
file_read
(
val
);
if
(
!
data
)
{
ret
=
AVERROR
(
EIO
);
goto
err_load
;
}
ret
=
av_opt_set
(
f
,
key
,
data
,
AV_OPT_SEARCH_CHILDREN
);
av_freep
(
&
data
);
}
if
(
ret
<
0
)
goto
err_apply
;
return
0
;
err_apply
:
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error applying option '%s' to filter '%s': %s
\n
"
,
key
,
f
->
filter
->
name
,
av_err2str
(
ret
));
return
ret
;
err_load
:
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error loading value for option '%s' from file '%s'
\n
"
,
key
,
val
);
return
ret
;
}
static
int
graph_opts_apply
(
AVFilterGraphSegment
*
seg
)
{
for
(
size_t
i
=
0
;
i
<
seg
->
nb_chains
;
i
++
)
{
AVFilterChain
*
ch
=
seg
->
chains
[
i
];
for
(
size_t
j
=
0
;
j
<
ch
->
nb_filters
;
j
++
)
{
AVFilterParams
*
p
=
ch
->
filters
[
j
];
const
AVDictionaryEntry
*
e
=
NULL
;
av_assert0
(
p
->
filter
);
while
((
e
=
av_dict_iterate
(
p
->
opts
,
e
)))
{
int
ret
=
filter_opt_apply
(
p
->
filter
,
e
->
key
,
e
->
value
);
if
(
ret
<
0
)
return
ret
;
}
av_dict_free
(
&
p
->
opts
);
}
}
return
0
;
}
static
int
graph_parse
(
AVFilterGraph
*
graph
,
const
char
*
desc
,
AVFilterInOut
**
inputs
,
AVFilterInOut
**
outputs
)
{
AVFilterGraphSegment
*
seg
;
int
ret
;
ret
=
avfilter_graph_segment_parse
(
graph
,
desc
,
0
,
&
seg
);
if
(
ret
<
0
)
return
ret
;
ret
=
avfilter_graph_segment_create_filters
(
seg
,
0
);
if
(
ret
<
0
)
goto
fail
;
ret
=
graph_opts_apply
(
seg
);
if
(
ret
<
0
)
goto
fail
;
ret
=
avfilter_graph_segment_apply
(
seg
,
0
,
inputs
,
outputs
);
fail
:
avfilter_graph_segment_free
(
&
seg
);
return
ret
;
}
int
init_complex_filtergraph
(
FilterGraph
*
fg
)
int
init_complex_filtergraph
(
FilterGraph
*
fg
)
{
{
AVFilterInOut
*
inputs
,
*
outputs
,
*
cur
;
AVFilterInOut
*
inputs
,
*
outputs
,
*
cur
;
...
@@ -342,7 +501,7 @@ int init_complex_filtergraph(FilterGraph *fg)
...
@@ -342,7 +501,7 @@ int init_complex_filtergraph(FilterGraph *fg)
return
AVERROR
(
ENOMEM
);
return
AVERROR
(
ENOMEM
);
graph
->
nb_threads
=
1
;
graph
->
nb_threads
=
1
;
ret
=
avfilter_graph_parse2
(
graph
,
fg
->
graph_desc
,
&
inputs
,
&
outputs
);
ret
=
graph_parse
(
graph
,
fg
->
graph_desc
,
&
inputs
,
&
outputs
);
if
(
ret
<
0
)
if
(
ret
<
0
)
goto
fail
;
goto
fail
;
...
@@ -467,8 +626,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter,
...
@@ -467,8 +626,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter,
snprintf
(
args
,
sizeof
(
args
),
"%d:%d"
,
snprintf
(
args
,
sizeof
(
args
),
"%d:%d"
,
ofilter
->
width
,
ofilter
->
height
);
ofilter
->
width
,
ofilter
->
height
);
while
((
e
=
av_dict_get
(
ost
->
sws_dict
,
""
,
e
,
while
((
e
=
av_dict_iterate
(
ost
->
sws_dict
,
e
)))
{
AV_DICT_IGNORE_SUFFIX
)))
{
av_strlcatf
(
args
,
sizeof
(
args
),
":%s=%s"
,
e
->
key
,
e
->
value
);
av_strlcatf
(
args
,
sizeof
(
args
),
":%s=%s"
,
e
->
key
,
e
->
value
);
}
}
...
@@ -575,6 +733,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
...
@@ -575,6 +733,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
pad_idx = 0; \
pad_idx = 0; \
} while (0)
} while (0)
av_bprint_init
(
&
args
,
0
,
AV_BPRINT_SIZE_UNLIMITED
);
av_bprint_init
(
&
args
,
0
,
AV_BPRINT_SIZE_UNLIMITED
);
#if FFMPEG_OPT_MAP_CHANNEL
if
(
ost
->
audio_channels_mapped
)
{
if
(
ost
->
audio_channels_mapped
)
{
AVChannelLayout
mapped_layout
=
{
0
};
AVChannelLayout
mapped_layout
=
{
0
};
int
i
;
int
i
;
...
@@ -587,6 +746,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
...
@@ -587,6 +746,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
AUTO_INSERT_FILTER
(
"-map_channel"
,
"pan"
,
args
.
str
);
AUTO_INSERT_FILTER
(
"-map_channel"
,
"pan"
,
args
.
str
);
av_bprint_clear
(
&
args
);
av_bprint_clear
(
&
args
);
}
}
#endif
if
(
codec
->
ch_layout
.
order
==
AV_CHANNEL_ORDER_UNSPEC
)
if
(
codec
->
ch_layout
.
order
==
AV_CHANNEL_ORDER_UNSPEC
)
av_channel_layout_default
(
&
codec
->
ch_layout
,
codec
->
ch_layout
.
nb_channels
);
av_channel_layout_default
(
&
codec
->
ch_layout
,
codec
->
ch_layout
.
nb_channels
);
...
@@ -620,11 +780,11 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
...
@@ -620,11 +780,11 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter,
if
(
ost
->
apad
&&
of
->
shortest
)
{
if
(
ost
->
apad
&&
of
->
shortest
)
{
int
i
;
int
i
;
for
(
i
=
0
;
i
<
of
->
ctx
->
nb_streams
;
i
++
)
for
(
i
=
0
;
i
<
of
->
nb_streams
;
i
++
)
if
(
of
->
ctx
->
streams
[
i
]
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
)
if
(
of
->
streams
[
i
]
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
)
break
;
break
;
if
(
i
<
of
->
ctx
->
nb_streams
)
{
if
(
i
<
of
->
nb_streams
)
{
AUTO_INSERT_FILTER
(
"-apad"
,
"apad"
,
ost
->
apad
);
AUTO_INSERT_FILTER
(
"-apad"
,
"apad"
,
ost
->
apad
);
}
}
}
}
...
@@ -751,7 +911,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
...
@@ -751,7 +911,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
}
}
if
(
!
fr
.
num
)
if
(
!
fr
.
num
)
fr
=
av_guess_frame_rate
(
input_files
[
ist
->
file_index
]
->
ctx
,
ist
->
st
,
NULL
)
;
fr
=
ist
->
framerate_guessed
;
if
(
ist
->
dec_ctx
->
codec_type
==
AVMEDIA_TYPE_SUBTITLE
)
{
if
(
ist
->
dec_ctx
->
codec_type
==
AVMEDIA_TYPE_SUBTITLE
)
{
ret
=
sub2video_prepare
(
ist
,
ifilter
);
ret
=
sub2video_prepare
(
ist
,
ifilter
);
...
@@ -904,40 +1064,6 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter,
...
@@ -904,40 +1064,6 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter,
last_filter = filt_ctx; \
last_filter = filt_ctx; \
} while (0)
} while (0)
if
(
audio_sync_method
>
0
)
{
char
args
[
256
]
=
{
0
};
av_strlcatf
(
args
,
sizeof
(
args
),
"async=%d"
,
audio_sync_method
);
if
(
audio_drift_threshold
!=
0
.
1
)
av_strlcatf
(
args
,
sizeof
(
args
),
":min_hard_comp=%f"
,
audio_drift_threshold
);
if
(
!
fg
->
reconfiguration
)
av_strlcatf
(
args
,
sizeof
(
args
),
":first_pts=0"
);
AUTO_INSERT_FILTER_INPUT
(
"-async"
,
"aresample"
,
args
);
}
// if (ost->audio_channels_mapped) {
// int i;
// AVBPrint pan_buf;
// av_bprint_init(&pan_buf, 256, 8192);
// av_bprintf(&pan_buf, "0x%"PRIx64,
// av_get_default_channel_layout(ost->audio_channels_mapped));
// for (i = 0; i < ost->audio_channels_mapped; i++)
// if (ost->audio_channels_map[i] != -1)
// av_bprintf(&pan_buf, ":c%d=c%d", i, ost->audio_channels_map[i]);
// AUTO_INSERT_FILTER_INPUT("-map_channel", "pan", pan_buf.str);
// av_bprint_finalize(&pan_buf, NULL);
// }
if
(
audio_volume
!=
256
)
{
char
args
[
256
];
av_log
(
NULL
,
AV_LOG_WARNING
,
"-vol has been deprecated. Use the volume "
"audio filter instead.
\n
"
);
snprintf
(
args
,
sizeof
(
args
),
"%f"
,
audio_volume
/
256
.);
AUTO_INSERT_FILTER_INPUT
(
"-vol"
,
"volume"
,
args
);
}
snprintf
(
name
,
sizeof
(
name
),
"trim for input stream %d:%d"
,
snprintf
(
name
,
sizeof
(
name
),
"trim for input stream %d:%d"
,
ist
->
file_index
,
ist
->
st
->
index
);
ist
->
file_index
,
ist
->
st
->
index
);
if
(
copy_ts
)
{
if
(
copy_ts
)
{
...
@@ -1020,44 +1146,39 @@ int configure_filtergraph(FilterGraph *fg)
...
@@ -1020,44 +1146,39 @@ int configure_filtergraph(FilterGraph *fg)
if
(
simple
)
{
if
(
simple
)
{
OutputStream
*
ost
=
fg
->
outputs
[
0
]
->
ost
;
OutputStream
*
ost
=
fg
->
outputs
[
0
]
->
ost
;
char
args
[
512
];
const
AVDictionaryEntry
*
e
=
NULL
;
if
(
filter_nbthreads
)
{
if
(
filter_nbthreads
)
{
ret
=
av_opt_set
(
fg
->
graph
,
"threads"
,
filter_nbthreads
,
0
);
ret
=
av_opt_set
(
fg
->
graph
,
"threads"
,
filter_nbthreads
,
0
);
if
(
ret
<
0
)
if
(
ret
<
0
)
goto
fail
;
goto
fail
;
}
else
{
}
else
{
const
AVDictionaryEntry
*
e
=
NULL
;
e
=
av_dict_get
(
ost
->
encoder_opts
,
"threads"
,
NULL
,
0
);
e
=
av_dict_get
(
ost
->
encoder_opts
,
"threads"
,
NULL
,
0
);
if
(
e
)
if
(
e
)
av_opt_set
(
fg
->
graph
,
"threads"
,
e
->
value
,
0
);
av_opt_set
(
fg
->
graph
,
"threads"
,
e
->
value
,
0
);
}
}
args
[
0
]
=
0
;
if
(
av_dict_count
(
ost
->
sws_dict
))
{
e
=
NULL
;
ret
=
av_dict_get_string
(
ost
->
sws_dict
,
while
((
e
=
av_dict_get
(
ost
->
sws_dict
,
""
,
e
,
&
fg
->
graph
->
scale_sws_opts
,
AV_DICT_IGNORE_SUFFIX
)))
{
'='
,
':'
);
av_strlcatf
(
args
,
sizeof
(
args
),
"%s=%s:"
,
e
->
key
,
e
->
value
);
if
(
ret
<
0
)
}
goto
fail
;
if
(
strlen
(
args
))
{
args
[
strlen
(
args
)
-
1
]
=
0
;
fg
->
graph
->
scale_sws_opts
=
av_strdup
(
args
);
}
}
args
[
0
]
=
0
;
if
(
av_dict_count
(
ost
->
swr_opts
))
{
e
=
NULL
;
char
*
args
;
while
((
e
=
av_dict_get
(
ost
->
swr_opts
,
""
,
e
,
ret
=
av_dict_get_string
(
ost
->
swr_opts
,
&
args
,
'='
,
':'
);
AV_DICT_IGNORE_SUFFIX
)))
{
if
(
ret
<
0
)
av_strlcatf
(
args
,
sizeof
(
args
),
"%s=%s:"
,
e
->
key
,
e
->
value
);
goto
fail
;
av_opt_set
(
fg
->
graph
,
"aresample_swr_opts"
,
args
,
0
);
av_free
(
args
);
}
}
if
(
strlen
(
args
))
args
[
strlen
(
args
)
-
1
]
=
0
;
av_opt_set
(
fg
->
graph
,
"aresample_swr_opts"
,
args
,
0
);
}
else
{
}
else
{
fg
->
graph
->
nb_threads
=
filter_complex_nbthreads
;
fg
->
graph
->
nb_threads
=
filter_complex_nbthreads
;
}
}
if
((
ret
=
avfilter_graph_parse2
(
fg
->
graph
,
graph_desc
,
&
inputs
,
&
outputs
))
<
0
)
if
((
ret
=
graph_parse
(
fg
->
graph
,
graph_desc
,
&
inputs
,
&
outputs
))
<
0
)
goto
fail
;
goto
fail
;
ret
=
hw_device_setup_for_filter
(
fg
);
ret
=
hw_device_setup_for_filter
(
fg
);
...
@@ -1131,16 +1252,8 @@ int configure_filtergraph(FilterGraph *fg)
...
@@ -1131,16 +1252,8 @@ int configure_filtergraph(FilterGraph *fg)
for
(
i
=
0
;
i
<
fg
->
nb_outputs
;
i
++
)
{
for
(
i
=
0
;
i
<
fg
->
nb_outputs
;
i
++
)
{
OutputStream
*
ost
=
fg
->
outputs
[
i
]
->
ost
;
OutputStream
*
ost
=
fg
->
outputs
[
i
]
->
ost
;
if
(
!
ost
->
enc
)
{
if
(
ost
->
enc_ctx
->
codec_type
==
AVMEDIA_TYPE_AUDIO
&&
/* identical to the same check in ffmpeg.c, needed because
!
(
ost
->
enc_ctx
->
codec
->
capabilities
&
AV_CODEC_CAP_VARIABLE_FRAME_SIZE
))
complex filter graphs are initialized earlier */
av_log
(
NULL
,
AV_LOG_ERROR
,
"Encoder (codec %s) not found for output stream #%d:%d
\n
"
,
avcodec_get_name
(
ost
->
st
->
codecpar
->
codec_id
),
ost
->
file_index
,
ost
->
index
);
ret
=
AVERROR
(
EINVAL
);
goto
fail
;
}
if
(
ost
->
enc
->
type
==
AVMEDIA_TYPE_AUDIO
&&
!
(
ost
->
enc
->
capabilities
&
AV_CODEC_CAP_VARIABLE_FRAME_SIZE
))
av_buffersink_set_frame_size
(
ost
->
filter
->
filter
,
av_buffersink_set_frame_size
(
ost
->
filter
->
filter
,
ost
->
enc_ctx
->
frame_size
);
ost
->
enc_ctx
->
frame_size
);
}
}
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_hw.c
浏览文件 @
f23762b9
/*
/*
* copyright (c) 2018 Taner Sener ( tanersener gmail com )
* Copyright (c) 2018-2019 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
*
*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
*
*
...
@@ -23,6 +24,12 @@
...
@@ -23,6 +24,12 @@
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
*
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
*
*
* 12.2019
* 12.2019
...
@@ -357,7 +364,7 @@ int hw_device_setup_for_decode(InputStream *ist)
...
@@ -357,7 +364,7 @@ int hw_device_setup_for_decode(InputStream *ist)
if
(
ist
->
hwaccel_id
==
HWACCEL_AUTO
)
{
if
(
ist
->
hwaccel_id
==
HWACCEL_AUTO
)
{
ist
->
hwaccel_device_type
=
dev
->
type
;
ist
->
hwaccel_device_type
=
dev
->
type
;
}
else
if
(
ist
->
hwaccel_device_type
!=
dev
->
type
)
{
}
else
if
(
ist
->
hwaccel_device_type
!=
dev
->
type
)
{
av_log
(
ist
->
dec_ctx
,
AV_LOG_ERROR
,
"Invalid hwaccel device "
av_log
(
NULL
,
AV_LOG_ERROR
,
"Invalid hwaccel device "
"specified for decoder: device %s of type %s is not "
"specified for decoder: device %s of type %s is not "
"usable with hwaccel %s.
\n
"
,
dev
->
name
,
"usable with hwaccel %s.
\n
"
,
dev
->
name
,
av_hwdevice_get_type_name
(
dev
->
type
),
av_hwdevice_get_type_name
(
dev
->
type
),
...
@@ -408,7 +415,7 @@ int hw_device_setup_for_decode(InputStream *ist)
...
@@ -408,7 +415,7 @@ int hw_device_setup_for_decode(InputStream *ist)
type
=
config
->
device_type
;
type
=
config
->
device_type
;
dev
=
hw_device_get_by_type
(
type
);
dev
=
hw_device_get_by_type
(
type
);
if
(
dev
)
{
if
(
dev
)
{
av_log
(
ist
->
dec_ctx
,
AV_LOG_INFO
,
"Using auto "
av_log
(
NULL
,
AV_LOG_INFO
,
"Using auto "
"hwaccel type %s with existing device %s.
\n
"
,
"hwaccel type %s with existing device %s.
\n
"
,
av_hwdevice_get_type_name
(
type
),
dev
->
name
);
av_hwdevice_get_type_name
(
type
),
dev
->
name
);
}
}
...
@@ -426,12 +433,12 @@ int hw_device_setup_for_decode(InputStream *ist)
...
@@ -426,12 +433,12 @@ int hw_device_setup_for_decode(InputStream *ist)
continue
;
continue
;
}
}
if
(
ist
->
hwaccel_device
)
{
if
(
ist
->
hwaccel_device
)
{
av_log
(
ist
->
dec_ctx
,
AV_LOG_INFO
,
"Using auto "
av_log
(
NULL
,
AV_LOG_INFO
,
"Using auto "
"hwaccel type %s with new device created "
"hwaccel type %s with new device created "
"from %s.
\n
"
,
av_hwdevice_get_type_name
(
type
),
"from %s.
\n
"
,
av_hwdevice_get_type_name
(
type
),
ist
->
hwaccel_device
);
ist
->
hwaccel_device
);
}
else
{
}
else
{
av_log
(
ist
->
dec_ctx
,
AV_LOG_INFO
,
"Using auto "
av_log
(
NULL
,
AV_LOG_INFO
,
"Using auto "
"hwaccel type %s with new default device.
\n
"
,
"hwaccel type %s with new default device.
\n
"
,
av_hwdevice_get_type_name
(
type
));
av_hwdevice_get_type_name
(
type
));
}
}
...
@@ -439,7 +446,7 @@ int hw_device_setup_for_decode(InputStream *ist)
...
@@ -439,7 +446,7 @@ int hw_device_setup_for_decode(InputStream *ist)
if
(
dev
)
{
if
(
dev
)
{
ist
->
hwaccel_device_type
=
type
;
ist
->
hwaccel_device_type
=
type
;
}
else
{
}
else
{
av_log
(
ist
->
dec_ctx
,
AV_LOG_INFO
,
"Auto hwaccel "
av_log
(
NULL
,
AV_LOG_INFO
,
"Auto hwaccel "
"disabled: no device found.
\n
"
);
"disabled: no device found.
\n
"
);
ist
->
hwaccel_id
=
HWACCEL_NONE
;
ist
->
hwaccel_id
=
HWACCEL_NONE
;
return
0
;
return
0
;
...
@@ -447,7 +454,7 @@ int hw_device_setup_for_decode(InputStream *ist)
...
@@ -447,7 +454,7 @@ int hw_device_setup_for_decode(InputStream *ist)
}
}
if
(
!
dev
)
{
if
(
!
dev
)
{
av_log
(
ist
->
dec_ctx
,
AV_LOG_ERROR
,
"No device available "
av_log
(
NULL
,
AV_LOG_ERROR
,
"No device available "
"for decoder: device type %s needed for codec %s.
\n
"
,
"for decoder: device type %s needed for codec %s.
\n
"
,
av_hwdevice_get_type_name
(
type
),
ist
->
dec
->
name
);
av_hwdevice_get_type_name
(
type
),
ist
->
dec
->
name
);
return
err
;
return
err
;
...
@@ -479,7 +486,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
...
@@ -479,7 +486,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
}
}
for
(
i
=
0
;;
i
++
)
{
for
(
i
=
0
;;
i
++
)
{
config
=
avcodec_get_hw_config
(
ost
->
enc
,
i
);
config
=
avcodec_get_hw_config
(
ost
->
enc
_ctx
->
codec
,
i
);
if
(
!
config
)
if
(
!
config
)
break
;
break
;
...
@@ -490,7 +497,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
...
@@ -490,7 +497,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
av_log
(
ost
->
enc_ctx
,
AV_LOG_VERBOSE
,
"Using input "
av_log
(
ost
->
enc_ctx
,
AV_LOG_VERBOSE
,
"Using input "
"frames context (format %s) with %s encoder.
\n
"
,
"frames context (format %s) with %s encoder.
\n
"
,
av_get_pix_fmt_name
(
ost
->
enc_ctx
->
pix_fmt
),
av_get_pix_fmt_name
(
ost
->
enc_ctx
->
pix_fmt
),
ost
->
enc
->
name
);
ost
->
enc
_ctx
->
codec
->
name
);
ost
->
enc_ctx
->
hw_frames_ctx
=
av_buffer_ref
(
frames_ref
);
ost
->
enc_ctx
->
hw_frames_ctx
=
av_buffer_ref
(
frames_ref
);
if
(
!
ost
->
enc_ctx
->
hw_frames_ctx
)
if
(
!
ost
->
enc_ctx
->
hw_frames_ctx
)
return
AVERROR
(
ENOMEM
);
return
AVERROR
(
ENOMEM
);
...
@@ -505,7 +512,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
...
@@ -505,7 +512,7 @@ int hw_device_setup_for_encode(OutputStream *ost)
if
(
dev
)
{
if
(
dev
)
{
av_log
(
ost
->
enc_ctx
,
AV_LOG_VERBOSE
,
"Using device %s "
av_log
(
ost
->
enc_ctx
,
AV_LOG_VERBOSE
,
"Using device %s "
"(type %s) with %s encoder.
\n
"
,
dev
->
name
,
"(type %s) with %s encoder.
\n
"
,
dev
->
name
,
av_hwdevice_get_type_name
(
dev
->
type
),
ost
->
enc
->
name
);
av_hwdevice_get_type_name
(
dev
->
type
),
ost
->
enc
_ctx
->
codec
->
name
);
ost
->
enc_ctx
->
hw_device_ctx
=
av_buffer_ref
(
dev
->
device_ref
);
ost
->
enc_ctx
->
hw_device_ctx
=
av_buffer_ref
(
dev
->
device_ref
);
if
(
!
ost
->
enc_ctx
->
hw_device_ctx
)
if
(
!
ost
->
enc_ctx
->
hw_device_ctx
)
return
AVERROR
(
ENOMEM
);
return
AVERROR
(
ENOMEM
);
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.c
浏览文件 @
f23762b9
/*
/*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
* copyright (c) 2022 Taner Sener ( tanersener gmail com )
* Copyright (c) 2022 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
*
*
* FFmpeg is free software; you can redistribute it and/or
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* modify it under the terms of the GNU Lesser General Public
...
@@ -22,108 +23,101 @@
...
@@ -22,108 +23,101 @@
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop the ffmpeg-kit library.
* by us to develop the ffmpeg-kit library.
*
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools header names updated
* - want_sdp marked as thread-local
* - ms_from_ost migrated from ffmpeg_mux.c and marked as non-static
*
* ffmpeg-kit changes by Taner Sener
* ffmpeg-kit changes by Taner Sener
*
*
* 09.2022
* 09.2022
* --------------------------------------------------------
* --------------------------------------------------------
* - fftools_ prefix added to fftools headers
* - fftools_ prefix added to fftools headers
* - using main_ffmpeg_return_code instead of main_return_code
* - using main_ffmpeg_return_code instead of main_return_code
* - printf replaced with av_log statements
*/
*/
#include <stdatomic.h>
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include "fftools_ffmpeg.h"
#include "fftools_ffmpeg.h"
#include "fftools_ffmpeg_mux.h"
#include "fftools_objpool.h"
#include "fftools_sync_queue.h"
#include "fftools_thread_queue.h"
#include "libavutil/fifo.h"
#include "libavutil/fifo.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "libavutil/mem.h"
#include "libavutil/timestamp.h"
#include "libavutil/timestamp.h"
#include "libavutil/thread.h"
#include "libavcodec/packet.h"
#include "libavcodec/packet.h"
#include "libavformat/avformat.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavformat/avio.h"
static
void
close_all_output_streams
(
OutputStream
*
ost
,
OSTFinished
this_stream
,
OSTFinished
others
)
__thread
int
want_sdp
=
1
;
MuxStream
*
ms_from_ost
(
OutputStream
*
ost
)
{
{
int
i
;
return
(
MuxStream
*
)
ost
;
for
(
i
=
0
;
i
<
nb_output_streams
;
i
++
)
{
}
OutputStream
*
ost2
=
output_streams
[
i
];
ost2
->
finished
|=
ost
==
ost2
?
this_stream
:
others
;
static
Muxer
*
mux_from_of
(
OutputFile
*
of
)
{
return
(
Muxer
*
)
of
;
}
static
int64_t
filesize
(
AVIOContext
*
pb
)
{
int64_t
ret
=
-
1
;
if
(
pb
)
{
ret
=
avio_size
(
pb
);
if
(
ret
<=
0
)
// FIXME improve avio_size() so it works with non seekable output too
ret
=
avio_tell
(
pb
);
}
}
return
ret
;
}
}
void
of_write_packet
(
OutputFile
*
of
,
AVPacket
*
pkt
,
OutputStream
*
ost
,
static
int
write_packet
(
Muxer
*
mux
,
OutputStream
*
ost
,
AVPacket
*
pkt
)
int
unqueue
)
{
{
AVFormatContext
*
s
=
of
->
ctx
;
MuxStream
*
ms
=
ms_from_ost
(
ost
);
AVFormatContext
*
s
=
mux
->
fc
;
AVStream
*
st
=
ost
->
st
;
AVStream
*
st
=
ost
->
st
;
int64_t
fs
;
uint64_t
frame_num
;
int
ret
;
int
ret
;
/*
fs
=
filesize
(
s
->
pb
);
* Audio encoders may split the packets -- #frames in != #packets out.
atomic_store
(
&
mux
->
last_filesize
,
fs
);
* But there is no reordering, so we can limit the number of output packets
if
(
fs
>=
mux
->
limit_filesize
)
{
* by simply dropping them here.
ret
=
AVERROR_EOF
;
* Counting encoded video frames needs to be done separately because of
goto
fail
;
* reordering, see do_video_out().
* Do not count the packet when unqueued because it has been counted when queued.
*/
if
(
!
(
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
&&
ost
->
encoding_needed
)
&&
!
unqueue
)
{
if
(
ost
->
frame_number
>=
ost
->
max_frames
)
{
av_packet_unref
(
pkt
);
return
;
}
ost
->
frame_number
++
;
}
if
(
!
of
->
header_written
)
{
AVPacket
*
tmp_pkt
;
/* the muxer is not initialized yet, buffer the packet */
if
(
!
av_fifo_can_write
(
ost
->
muxing_queue
))
{
size_t
cur_size
=
av_fifo_can_read
(
ost
->
muxing_queue
);
unsigned
int
are_we_over_size
=
(
ost
->
muxing_queue_data_size
+
pkt
->
size
)
>
ost
->
muxing_queue_data_threshold
;
size_t
limit
=
are_we_over_size
?
ost
->
max_muxing_queue_size
:
SIZE_MAX
;
size_t
new_size
=
FFMIN
(
2
*
cur_size
,
limit
);
if
(
new_size
<=
cur_size
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Too many packets buffered for output stream %d:%d.
\n
"
,
ost
->
file_index
,
ost
->
st
->
index
);
exit_program
(
1
);
}
ret
=
av_fifo_grow2
(
ost
->
muxing_queue
,
new_size
-
cur_size
);
if
(
ret
<
0
)
exit_program
(
1
);
}
ret
=
av_packet_make_refcounted
(
pkt
);
if
(
ret
<
0
)
exit_program
(
1
);
tmp_pkt
=
av_packet_alloc
();
if
(
!
tmp_pkt
)
exit_program
(
1
);
av_packet_move_ref
(
tmp_pkt
,
pkt
);
ost
->
muxing_queue_data_size
+=
tmp_pkt
->
size
;
av_fifo_write
(
ost
->
muxing_queue
,
&
tmp_pkt
,
1
);
return
;
}
}
if
((
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
&&
ost
->
vsync_method
==
VSYNC_DROP
)
||
if
(
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
&&
ost
->
vsync_method
==
VSYNC_DROP
)
(
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_AUDIO
&&
audio_sync_method
<
0
))
pkt
->
pts
=
pkt
->
dts
=
AV_NOPTS_VALUE
;
pkt
->
pts
=
pkt
->
dts
=
AV_NOPTS_VALUE
;
if
(
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
)
{
if
(
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
)
{
if
(
ost
->
frame_rate
.
num
&&
ost
->
is_cfr
)
{
if
(
ost
->
frame_rate
.
num
&&
ost
->
is_cfr
)
{
if
(
pkt
->
duration
>
0
)
if
(
pkt
->
duration
>
0
)
av_log
(
NULL
,
AV_LOG_WARNING
,
"Overriding packet duration by frame rate, this should not happen
\n
"
);
av_log
(
ost
,
AV_LOG_WARNING
,
"Overriding packet duration by frame rate, this should not happen
\n
"
);
pkt
->
duration
=
av_rescale_q
(
1
,
av_inv_q
(
ost
->
frame_rate
),
pkt
->
duration
=
av_rescale_q
(
1
,
av_inv_q
(
ost
->
frame_rate
),
ost
->
mux_time
base
);
pkt
->
time_
base
);
}
}
}
}
av_packet_rescale_ts
(
pkt
,
ost
->
mux_timebase
,
ost
->
st
->
time_base
);
av_packet_rescale_ts
(
pkt
,
pkt
->
time_base
,
ost
->
st
->
time_base
);
pkt
->
time_base
=
ost
->
st
->
time_base
;
if
(
!
(
s
->
oformat
->
flags
&
AVFMT_NOTIMESTAMPS
))
{
if
(
!
(
s
->
oformat
->
flags
&
AVFMT_NOTIMESTAMPS
))
{
if
(
pkt
->
dts
!=
AV_NOPTS_VALUE
&&
if
(
pkt
->
dts
!=
AV_NOPTS_VALUE
&&
...
@@ -133,25 +127,26 @@ void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
...
@@ -133,25 +127,26 @@ void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
pkt
->
dts
,
pkt
->
pts
,
pkt
->
dts
,
pkt
->
pts
,
ost
->
file_index
,
ost
->
st
->
index
);
ost
->
file_index
,
ost
->
st
->
index
);
pkt
->
pts
=
pkt
->
pts
=
pkt
->
dts
=
pkt
->
pts
+
pkt
->
dts
+
ost
->
last_mux_dts
+
1
pkt
->
dts
=
pkt
->
pts
+
pkt
->
dts
+
ms
->
last_mux_dts
+
1
-
FFMIN3
(
pkt
->
pts
,
pkt
->
dts
,
ost
->
last_mux_dts
+
1
)
-
FFMIN3
(
pkt
->
pts
,
pkt
->
dts
,
ms
->
last_mux_dts
+
1
)
-
FFMAX3
(
pkt
->
pts
,
pkt
->
dts
,
ost
->
last_mux_dts
+
1
);
-
FFMAX3
(
pkt
->
pts
,
pkt
->
dts
,
ms
->
last_mux_dts
+
1
);
}
}
if
((
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_AUDIO
||
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
||
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_SUBTITLE
)
&&
if
((
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_AUDIO
||
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
||
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_SUBTITLE
)
&&
pkt
->
dts
!=
AV_NOPTS_VALUE
&&
pkt
->
dts
!=
AV_NOPTS_VALUE
&&
ost
->
last_mux_dts
!=
AV_NOPTS_VALUE
)
{
ms
->
last_mux_dts
!=
AV_NOPTS_VALUE
)
{
int64_t
max
=
ost
->
last_mux_dts
+
!
(
s
->
oformat
->
flags
&
AVFMT_TS_NONSTRICT
);
int64_t
max
=
ms
->
last_mux_dts
+
!
(
s
->
oformat
->
flags
&
AVFMT_TS_NONSTRICT
);
if
(
pkt
->
dts
<
max
)
{
if
(
pkt
->
dts
<
max
)
{
int
loglevel
=
max
-
pkt
->
dts
>
2
||
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
?
AV_LOG_WARNING
:
AV_LOG_DEBUG
;
int
loglevel
=
max
-
pkt
->
dts
>
2
||
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
?
AV_LOG_WARNING
:
AV_LOG_DEBUG
;
if
(
exit_on_error
)
if
(
exit_on_error
)
loglevel
=
AV_LOG_ERROR
;
loglevel
=
AV_LOG_ERROR
;
av_log
(
s
,
loglevel
,
"Non-monotonous DTS in output stream "
av_log
(
s
,
loglevel
,
"Non-monotonous DTS in output stream "
"%d:%d; previous: %"
PRId64
", current: %"
PRId64
"; "
,
"%d:%d; previous: %"
PRId64
", current: %"
PRId64
"; "
,
ost
->
file_index
,
ost
->
st
->
index
,
ost
->
last_mux_dts
,
pkt
->
dts
);
ost
->
file_index
,
ost
->
st
->
index
,
ms
->
last_mux_dts
,
pkt
->
dts
);
if
(
exit_on_error
)
{
if
(
exit_on_error
)
{
av_log
(
NULL
,
AV_LOG_FATAL
,
"aborting.
\n
"
);
ret
=
AVERROR
(
EINVAL
);
exit_program
(
1
)
;
goto
fail
;
}
}
av_log
(
s
,
loglevel
,
"changing to %"
PRId64
". This may result "
av_log
(
s
,
loglevel
,
"changing to %"
PRId64
". This may result "
"in incorrect timestamps in the output file.
\n
"
,
"in incorrect timestamps in the output file.
\n
"
,
max
);
max
);
...
@@ -161,17 +156,17 @@ void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
...
@@ -161,17 +156,17 @@ void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
}
}
}
}
}
}
ost
->
last_mux_dts
=
pkt
->
dts
;
ms
->
last_mux_dts
=
pkt
->
dts
;
ost
->
data_size
+=
pkt
->
size
;
ost
->
data_size
_mux
+=
pkt
->
size
;
ost
->
packets_written
++
;
frame_num
=
atomic_fetch_add
(
&
ost
->
packets_written
,
1
)
;
pkt
->
stream_index
=
ost
->
index
;
pkt
->
stream_index
=
ost
->
index
;
if
(
debug_ts
)
{
if
(
debug_ts
)
{
av_log
(
NULL
,
AV_LOG_INFO
,
"muxer <- type:%s "
av_log
(
ost
,
AV_LOG_INFO
,
"muxer <- type:%s "
"pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d
\n
"
,
"pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d
\n
"
,
av_get_media_type_string
(
ost
->
enc_ctx
->
codec_type
),
av_get_media_type_string
(
st
->
codecpar
->
codec_type
),
av_ts2str
(
pkt
->
pts
),
av_ts2timestr
(
pkt
->
pts
,
&
ost
->
st
->
time_base
),
av_ts2str
(
pkt
->
pts
),
av_ts2timestr
(
pkt
->
pts
,
&
ost
->
st
->
time_base
),
av_ts2str
(
pkt
->
dts
),
av_ts2timestr
(
pkt
->
dts
,
&
ost
->
st
->
time_base
),
av_ts2str
(
pkt
->
dts
),
av_ts2timestr
(
pkt
->
dts
,
&
ost
->
st
->
time_base
),
av_ts2str
(
pkt
->
duration
),
av_ts2timestr
(
pkt
->
duration
,
&
ost
->
st
->
time_base
),
av_ts2str
(
pkt
->
duration
),
av_ts2timestr
(
pkt
->
duration
,
&
ost
->
st
->
time_base
),
...
@@ -179,12 +174,307 @@ void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
...
@@ -179,12 +174,307 @@ void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
);
);
}
}
if
(
ms
->
stats
.
io
)
enc_stats_write
(
ost
,
&
ms
->
stats
,
NULL
,
pkt
,
frame_num
);
ret
=
av_interleaved_write_frame
(
s
,
pkt
);
ret
=
av_interleaved_write_frame
(
s
,
pkt
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
print_error
(
"av_interleaved_write_frame()"
,
ret
);
print_error
(
"av_interleaved_write_frame()"
,
ret
);
main_ffmpeg_return_code
=
1
;
goto
fail
;
close_all_output_streams
(
ost
,
MUXER_FINISHED
|
ENCODER_FINISHED
,
ENCODER_FINISHED
);
}
}
return
0
;
fail:
av_packet_unref
(
pkt
);
return
ret
;
}
static
int
sync_queue_process
(
Muxer
*
mux
,
OutputStream
*
ost
,
AVPacket
*
pkt
,
int
*
stream_eof
)
{
OutputFile
*
of
=
&
mux
->
of
;
if
(
ost
->
sq_idx_mux
>=
0
)
{
int
ret
=
sq_send
(
mux
->
sq_mux
,
ost
->
sq_idx_mux
,
SQPKT
(
pkt
));
if
(
ret
<
0
)
{
if
(
ret
==
AVERROR_EOF
)
*
stream_eof
=
1
;
return
ret
;
}
while
(
1
)
{
ret
=
sq_receive
(
mux
->
sq_mux
,
-
1
,
SQPKT
(
mux
->
sq_pkt
));
if
(
ret
<
0
)
return
(
ret
==
AVERROR_EOF
||
ret
==
AVERROR
(
EAGAIN
))
?
0
:
ret
;
ret
=
write_packet
(
mux
,
of
->
streams
[
ret
],
mux
->
sq_pkt
);
if
(
ret
<
0
)
return
ret
;
}
}
else
if
(
pkt
)
return
write_packet
(
mux
,
ost
,
pkt
);
return
0
;
}
static
void
thread_set_name
(
OutputFile
*
of
)
{
char
name
[
16
];
snprintf
(
name
,
sizeof
(
name
),
"mux%d:%s"
,
of
->
index
,
of
->
format
->
name
);
ff_thread_setname
(
name
);
}
static
void
*
muxer_thread
(
void
*
arg
)
{
Muxer
*
mux
=
arg
;
OutputFile
*
of
=
&
mux
->
of
;
AVPacket
*
pkt
=
NULL
;
int
ret
=
0
;
pkt
=
av_packet_alloc
();
if
(
!
pkt
)
{
ret
=
AVERROR
(
ENOMEM
);
goto
finish
;
}
thread_set_name
(
of
);
while
(
1
)
{
OutputStream
*
ost
;
int
stream_idx
,
stream_eof
=
0
;
ret
=
tq_receive
(
mux
->
tq
,
&
stream_idx
,
pkt
);
if
(
stream_idx
<
0
)
{
av_log
(
mux
,
AV_LOG_VERBOSE
,
"All streams finished
\n
"
);
ret
=
0
;
break
;
}
ost
=
of
->
streams
[
stream_idx
];
ret
=
sync_queue_process
(
mux
,
ost
,
ret
<
0
?
NULL
:
pkt
,
&
stream_eof
);
av_packet_unref
(
pkt
);
if
(
ret
==
AVERROR_EOF
&&
stream_eof
)
tq_receive_finish
(
mux
->
tq
,
stream_idx
);
else
if
(
ret
<
0
)
{
av_log
(
mux
,
AV_LOG_ERROR
,
"Error muxing a packet
\n
"
);
break
;
}
}
finish:
av_packet_free
(
&
pkt
);
for
(
unsigned
int
i
=
0
;
i
<
mux
->
fc
->
nb_streams
;
i
++
)
tq_receive_finish
(
mux
->
tq
,
i
);
av_log
(
mux
,
AV_LOG_VERBOSE
,
"Terminating muxer thread
\n
"
);
return
(
void
*
)(
intptr_t
)
ret
;
}
static
int
thread_submit_packet
(
Muxer
*
mux
,
OutputStream
*
ost
,
AVPacket
*
pkt
)
{
int
ret
=
0
;
if
(
!
pkt
||
ost
->
finished
&
MUXER_FINISHED
)
goto
finish
;
ret
=
tq_send
(
mux
->
tq
,
ost
->
index
,
pkt
);
if
(
ret
<
0
)
goto
finish
;
return
0
;
finish:
if
(
pkt
)
av_packet_unref
(
pkt
);
ost
->
finished
|=
MUXER_FINISHED
;
tq_send_finish
(
mux
->
tq
,
ost
->
index
);
return
ret
==
AVERROR_EOF
?
0
:
ret
;
}
static
int
queue_packet
(
Muxer
*
mux
,
OutputStream
*
ost
,
AVPacket
*
pkt
)
{
MuxStream
*
ms
=
ms_from_ost
(
ost
);
AVPacket
*
tmp_pkt
=
NULL
;
int
ret
;
if
(
!
av_fifo_can_write
(
ms
->
muxing_queue
))
{
size_t
cur_size
=
av_fifo_can_read
(
ms
->
muxing_queue
);
size_t
pkt_size
=
pkt
?
pkt
->
size
:
0
;
unsigned
int
are_we_over_size
=
(
ms
->
muxing_queue_data_size
+
pkt_size
)
>
ms
->
muxing_queue_data_threshold
;
size_t
limit
=
are_we_over_size
?
ms
->
max_muxing_queue_size
:
SIZE_MAX
;
size_t
new_size
=
FFMIN
(
2
*
cur_size
,
limit
);
if
(
new_size
<=
cur_size
)
{
av_log
(
ost
,
AV_LOG_ERROR
,
"Too many packets buffered for output stream %d:%d.
\n
"
,
ost
->
file_index
,
ost
->
st
->
index
);
return
AVERROR
(
ENOSPC
);
}
ret
=
av_fifo_grow2
(
ms
->
muxing_queue
,
new_size
-
cur_size
);
if
(
ret
<
0
)
return
ret
;
}
if
(
pkt
)
{
ret
=
av_packet_make_refcounted
(
pkt
);
if
(
ret
<
0
)
return
ret
;
tmp_pkt
=
av_packet_alloc
();
if
(
!
tmp_pkt
)
return
AVERROR
(
ENOMEM
);
av_packet_move_ref
(
tmp_pkt
,
pkt
);
ms
->
muxing_queue_data_size
+=
tmp_pkt
->
size
;
}
av_fifo_write
(
ms
->
muxing_queue
,
&
tmp_pkt
,
1
);
return
0
;
}
static
int
submit_packet
(
Muxer
*
mux
,
AVPacket
*
pkt
,
OutputStream
*
ost
)
{
int
ret
;
if
(
mux
->
tq
)
{
return
thread_submit_packet
(
mux
,
ost
,
pkt
);
}
else
{
/* the muxer is not initialized yet, buffer the packet */
ret
=
queue_packet
(
mux
,
ost
,
pkt
);
if
(
ret
<
0
)
{
if
(
pkt
)
av_packet_unref
(
pkt
);
return
ret
;
}
}
return
0
;
}
void
of_output_packet
(
OutputFile
*
of
,
AVPacket
*
pkt
,
OutputStream
*
ost
,
int
eof
)
{
Muxer
*
mux
=
mux_from_of
(
of
);
MuxStream
*
ms
=
ms_from_ost
(
ost
);
const
char
*
err_msg
;
int
ret
=
0
;
if
(
!
eof
&&
pkt
->
dts
!=
AV_NOPTS_VALUE
)
ost
->
last_mux_dts
=
av_rescale_q
(
pkt
->
dts
,
pkt
->
time_base
,
AV_TIME_BASE_Q
);
/* apply the output bitstream filters */
if
(
ms
->
bsf_ctx
)
{
int
bsf_eof
=
0
;
ret
=
av_bsf_send_packet
(
ms
->
bsf_ctx
,
eof
?
NULL
:
pkt
);
if
(
ret
<
0
)
{
err_msg
=
"submitting a packet for bitstream filtering"
;
goto
fail
;
}
while
(
!
bsf_eof
)
{
ret
=
av_bsf_receive_packet
(
ms
->
bsf_ctx
,
pkt
);
if
(
ret
==
AVERROR
(
EAGAIN
))
return
;
else
if
(
ret
==
AVERROR_EOF
)
bsf_eof
=
1
;
else
if
(
ret
<
0
)
{
err_msg
=
"applying bitstream filters to a packet"
;
goto
fail
;
}
ret
=
submit_packet
(
mux
,
bsf_eof
?
NULL
:
pkt
,
ost
);
if
(
ret
<
0
)
goto
mux_fail
;
}
}
else
{
ret
=
submit_packet
(
mux
,
eof
?
NULL
:
pkt
,
ost
);
if
(
ret
<
0
)
goto
mux_fail
;
}
return
;
mux_fail:
err_msg
=
"submitting a packet to the muxer"
;
fail:
av_log
(
ost
,
AV_LOG_ERROR
,
"Error %s
\n
"
,
err_msg
);
if
(
exit_on_error
)
exit_program
(
1
);
}
static
int
thread_stop
(
Muxer
*
mux
)
{
void
*
ret
;
if
(
!
mux
||
!
mux
->
tq
)
return
0
;
for
(
unsigned
int
i
=
0
;
i
<
mux
->
fc
->
nb_streams
;
i
++
)
tq_send_finish
(
mux
->
tq
,
i
);
pthread_join
(
mux
->
thread
,
&
ret
);
tq_free
(
&
mux
->
tq
);
return
(
int
)(
intptr_t
)
ret
;
}
static
void
pkt_move
(
void
*
dst
,
void
*
src
)
{
av_packet_move_ref
(
dst
,
src
);
}
static
int
thread_start
(
Muxer
*
mux
)
{
AVFormatContext
*
fc
=
mux
->
fc
;
ObjPool
*
op
;
int
ret
;
op
=
objpool_alloc_packets
();
if
(
!
op
)
return
AVERROR
(
ENOMEM
);
mux
->
tq
=
tq_alloc
(
fc
->
nb_streams
,
mux
->
thread_queue_size
,
op
,
pkt_move
);
if
(
!
mux
->
tq
)
{
objpool_free
(
&
op
);
return
AVERROR
(
ENOMEM
);
}
ret
=
pthread_create
(
&
mux
->
thread
,
NULL
,
muxer_thread
,
(
void
*
)
mux
);
if
(
ret
)
{
tq_free
(
&
mux
->
tq
);
return
AVERROR
(
ret
);
}
/* flush the muxing queues */
for
(
int
i
=
0
;
i
<
fc
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
mux
->
of
.
streams
[
i
];
MuxStream
*
ms
=
ms_from_ost
(
ost
);
AVPacket
*
pkt
;
/* try to improve muxing time_base (only possible if nothing has been written yet) */
if
(
!
av_fifo_can_read
(
ms
->
muxing_queue
))
ost
->
mux_timebase
=
ost
->
st
->
time_base
;
while
(
av_fifo_read
(
ms
->
muxing_queue
,
&
pkt
,
1
)
>=
0
)
{
ret
=
thread_submit_packet
(
mux
,
ost
,
pkt
);
if
(
pkt
)
{
ms
->
muxing_queue_data_size
-=
pkt
->
size
;
av_packet_free
(
&
pkt
);
}
if
(
ret
<
0
)
return
ret
;
}
}
return
0
;
}
}
static
int
print_sdp
(
void
)
static
int
print_sdp
(
void
)
...
@@ -196,16 +486,16 @@ static int print_sdp(void)
...
@@ -196,16 +486,16 @@ static int print_sdp(void)
AVFormatContext
**
avc
;
AVFormatContext
**
avc
;
for
(
i
=
0
;
i
<
nb_output_files
;
i
++
)
{
for
(
i
=
0
;
i
<
nb_output_files
;
i
++
)
{
if
(
!
output_files
[
i
]
->
header_written
)
if
(
!
mux_from_of
(
output_files
[
i
])
->
header_written
)
return
0
;
return
0
;
}
}
avc
=
av_malloc_array
(
nb_output_files
,
sizeof
(
*
avc
));
avc
=
av_malloc_array
(
nb_output_files
,
sizeof
(
*
avc
));
if
(
!
avc
)
if
(
!
avc
)
exit_program
(
1
);
return
AVERROR
(
ENOMEM
);
for
(
i
=
0
,
j
=
0
;
i
<
nb_output_files
;
i
++
)
{
for
(
i
=
0
,
j
=
0
;
i
<
nb_output_files
;
i
++
)
{
if
(
!
strcmp
(
output_files
[
i
]
->
ctx
->
o
format
->
name
,
"rtp"
))
{
if
(
!
strcmp
(
output_files
[
i
]
->
format
->
name
,
"rtp"
))
{
avc
[
j
]
=
output_files
[
i
]
->
ctx
;
avc
[
j
]
=
mux_from_of
(
output_files
[
i
])
->
fc
;
j
++
;
j
++
;
}
}
}
}
...
@@ -221,7 +511,7 @@ static int print_sdp(void)
...
@@ -221,7 +511,7 @@ static int print_sdp(void)
goto
fail
;
goto
fail
;
if
(
!
sdp_filename
)
{
if
(
!
sdp_filename
)
{
printf
(
"SDP:
\n
%s
\n
"
,
sdp
);
av_log
(
NULL
,
AV_LOG_ERROR
,
"SDP:
\n
%s
\n
"
,
sdp
);
fflush
(
stdout
);
fflush
(
stdout
);
}
else
{
}
else
{
ret
=
avio_open2
(
&
sdp_pb
,
sdp_filename
,
AVIO_FLAG_WRITE
,
&
int_cb
,
NULL
);
ret
=
avio_open2
(
&
sdp_pb
,
sdp_filename
,
AVIO_FLAG_WRITE
,
&
int_cb
,
NULL
);
...
@@ -235,34 +525,36 @@ static int print_sdp(void)
...
@@ -235,34 +525,36 @@ static int print_sdp(void)
av_freep
(
&
sdp_filename
);
av_freep
(
&
sdp_filename
);
}
}
// SDP successfully written, allow muxer threads to start
ret
=
1
;
fail:
fail:
av_freep
(
&
avc
);
av_freep
(
&
avc
);
return
ret
;
return
ret
;
}
}
/* open the muxer when all the streams are initialized */
int
mux_check_init
(
Muxer
*
mux
)
int
of_check_init
(
OutputFile
*
of
)
{
{
OutputFile
*
of
=
&
mux
->
of
;
AVFormatContext
*
fc
=
mux
->
fc
;
int
ret
,
i
;
int
ret
,
i
;
for
(
i
=
0
;
i
<
of
->
ctx
->
nb_streams
;
i
++
)
{
for
(
i
=
0
;
i
<
fc
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
o
utput_streams
[
of
->
ost_index
+
i
];
OutputStream
*
ost
=
o
f
->
streams
[
i
];
if
(
!
ost
->
initialized
)
if
(
!
ost
->
initialized
)
return
0
;
return
0
;
}
}
ret
=
avformat_write_header
(
of
->
ctx
,
&
of
->
opts
);
ret
=
avformat_write_header
(
fc
,
&
mux
->
opts
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
av_log
(
mux
,
AV_LOG_ERROR
,
"Could not write header (incorrect codec "
"Could not write header for output file #%d "
"parameters ?): %s
\n
"
,
av_err2str
(
ret
));
"(incorrect codec parameters ?): %s
\n
"
,
of
->
index
,
av_err2str
(
ret
));
return
ret
;
return
ret
;
}
}
//assert_avoptions(of->opts);
//assert_avoptions(of->opts);
of
->
header_written
=
1
;
mux
->
header_written
=
1
;
av_dump_format
(
of
->
ctx
,
of
->
index
,
of
->
ctx
->
url
,
1
);
av_dump_format
(
fc
,
of
->
index
,
fc
->
url
,
1
);
nb_output_dumped
++
;
nb_output_dumped
++
;
if
(
sdp_filename
||
want_sdp
)
{
if
(
sdp_filename
||
want_sdp
)
{
...
@@ -270,62 +562,220 @@ int of_check_init(OutputFile *of)
...
@@ -270,62 +562,220 @@ int of_check_init(OutputFile *of)
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error writing the SDP.
\n
"
);
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error writing the SDP.
\n
"
);
return
ret
;
return
ret
;
}
else
if
(
ret
==
1
)
{
/* SDP is written only after all the muxers are ready, so now we
* start ALL the threads */
for
(
i
=
0
;
i
<
nb_output_files
;
i
++
)
{
ret
=
thread_start
(
mux_from_of
(
output_files
[
i
]));
if
(
ret
<
0
)
return
ret
;
}
}
}
}
else
{
ret
=
thread_start
(
mux_from_of
(
of
));
if
(
ret
<
0
)
return
ret
;
}
}
/* flush the muxing queues */
return
0
;
for
(
i
=
0
;
i
<
of
->
ctx
->
nb_streams
;
i
++
)
{
}
OutputStream
*
ost
=
output_streams
[
of
->
ost_index
+
i
];
AVPacket
*
pkt
;
/* try to improve muxing time_base (only possible if nothing has been written yet) */
static
int
bsf_init
(
MuxStream
*
ms
)
if
(
!
av_fifo_can_read
(
ost
->
muxing_queue
))
{
ost
->
mux_timebase
=
ost
->
st
->
time_base
;
OutputStream
*
ost
=
&
ms
->
ost
;
AVBSFContext
*
ctx
=
ms
->
bsf_ctx
;
int
ret
;
while
(
av_fifo_read
(
ost
->
muxing_queue
,
&
pkt
,
1
)
>=
0
)
{
if
(
!
ctx
)
ost
->
muxing_queue_data_size
-=
pkt
->
size
;
return
0
;
of_write_packet
(
of
,
pkt
,
ost
,
1
);
av_packet_free
(
&
pkt
);
ret
=
avcodec_parameters_copy
(
ctx
->
par_in
,
ost
->
st
->
codecpar
);
}
if
(
ret
<
0
)
return
ret
;
ctx
->
time_base_in
=
ost
->
st
->
time_base
;
ret
=
av_bsf_init
(
ctx
);
if
(
ret
<
0
)
{
av_log
(
ms
,
AV_LOG_ERROR
,
"Error initializing bitstream filter: %s
\n
"
,
ctx
->
filter
->
name
);
return
ret
;
}
}
ret
=
avcodec_parameters_copy
(
ost
->
st
->
codecpar
,
ctx
->
par_out
);
if
(
ret
<
0
)
return
ret
;
ost
->
st
->
time_base
=
ctx
->
time_base_out
;
return
0
;
return
0
;
}
}
int
of_stream_init
(
OutputFile
*
of
,
OutputStream
*
ost
)
{
Muxer
*
mux
=
mux_from_of
(
of
);
MuxStream
*
ms
=
ms_from_ost
(
ost
);
int
ret
;
if
(
ost
->
sq_idx_mux
>=
0
)
sq_set_tb
(
mux
->
sq_mux
,
ost
->
sq_idx_mux
,
ost
->
mux_timebase
);
/* initialize bitstream filters for the output stream
* needs to be done here, because the codec id for streamcopy is not
* known until now */
ret
=
bsf_init
(
ms
);
if
(
ret
<
0
)
return
ret
;
ost
->
initialized
=
1
;
return
mux_check_init
(
mux
);
}
int
of_write_trailer
(
OutputFile
*
of
)
int
of_write_trailer
(
OutputFile
*
of
)
{
{
Muxer
*
mux
=
mux_from_of
(
of
);
AVFormatContext
*
fc
=
mux
->
fc
;
int
ret
;
int
ret
;
if
(
!
of
->
header_written
)
{
if
(
!
mux
->
tq
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
av_log
(
mux
,
AV_LOG_ERROR
,
"Nothing was written into output file %d (%s), because "
"Nothing was written into output file, because "
"at least one of its streams received no packets.
\n
"
,
"at least one of its streams received no packets.
\n
"
);
of
->
index
,
of
->
ctx
->
url
);
return
AVERROR
(
EINVAL
);
return
AVERROR
(
EINVAL
);
}
}
ret
=
av_write_trailer
(
of
->
ctx
);
ret
=
thread_stop
(
mux
);
if
(
ret
<
0
)
main_ffmpeg_return_code
=
ret
;
ret
=
av_write_trailer
(
fc
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error writing trailer of %s: %s
\n
"
,
of
->
ctx
->
url
,
av_err2str
(
ret
));
av_log
(
mux
,
AV_LOG_ERROR
,
"Error writing trailer: %s
\n
"
,
av_err2str
(
ret
));
return
ret
;
return
ret
;
}
}
mux
->
last_filesize
=
filesize
(
fc
->
pb
);
if
(
!
(
of
->
format
->
flags
&
AVFMT_NOFILE
))
{
ret
=
avio_closep
(
&
fc
->
pb
);
if
(
ret
<
0
)
{
av_log
(
mux
,
AV_LOG_ERROR
,
"Error closing file: %s
\n
"
,
av_err2str
(
ret
));
return
ret
;
}
}
return
0
;
return
0
;
}
}
static
void
ost_free
(
OutputStream
**
post
)
{
OutputStream
*
ost
=
*
post
;
MuxStream
*
ms
;
if
(
!
ost
)
return
;
ms
=
ms_from_ost
(
ost
);
if
(
ost
->
logfile
)
{
if
(
fclose
(
ost
->
logfile
))
av_log
(
ms
,
AV_LOG_ERROR
,
"Error closing logfile, loss of information possible: %s
\n
"
,
av_err2str
(
AVERROR
(
errno
)));
ost
->
logfile
=
NULL
;
}
if
(
ms
->
muxing_queue
)
{
AVPacket
*
pkt
;
while
(
av_fifo_read
(
ms
->
muxing_queue
,
&
pkt
,
1
)
>=
0
)
av_packet_free
(
&
pkt
);
av_fifo_freep2
(
&
ms
->
muxing_queue
);
}
av_bsf_free
(
&
ms
->
bsf_ctx
);
av_frame_free
(
&
ost
->
filtered_frame
);
av_frame_free
(
&
ost
->
sq_frame
);
av_frame_free
(
&
ost
->
last_frame
);
av_packet_free
(
&
ost
->
pkt
);
av_dict_free
(
&
ost
->
encoder_opts
);
av_freep
(
&
ost
->
kf
.
pts
);
av_expr_free
(
ost
->
kf
.
pexpr
);
av_freep
(
&
ost
->
avfilter
);
av_freep
(
&
ost
->
logfile_prefix
);
av_freep
(
&
ost
->
apad
);
#if FFMPEG_OPT_MAP_CHANNEL
av_freep
(
&
ost
->
audio_channels_map
);
ost
->
audio_channels_mapped
=
0
;
#endif
av_dict_free
(
&
ost
->
sws_dict
);
av_dict_free
(
&
ost
->
swr_opts
);
if
(
ost
->
enc_ctx
)
av_freep
(
&
ost
->
enc_ctx
->
stats_in
);
avcodec_free_context
(
&
ost
->
enc_ctx
);
for
(
int
i
=
0
;
i
<
ost
->
enc_stats_pre
.
nb_components
;
i
++
)
av_freep
(
&
ost
->
enc_stats_pre
.
components
[
i
].
str
);
av_freep
(
&
ost
->
enc_stats_pre
.
components
);
for
(
int
i
=
0
;
i
<
ost
->
enc_stats_post
.
nb_components
;
i
++
)
av_freep
(
&
ost
->
enc_stats_post
.
components
[
i
].
str
);
av_freep
(
&
ost
->
enc_stats_post
.
components
);
for
(
int
i
=
0
;
i
<
ms
->
stats
.
nb_components
;
i
++
)
av_freep
(
&
ms
->
stats
.
components
[
i
].
str
);
av_freep
(
&
ms
->
stats
.
components
);
av_freep
(
post
);
}
static
void
fc_close
(
AVFormatContext
**
pfc
)
{
AVFormatContext
*
fc
=
*
pfc
;
if
(
!
fc
)
return
;
if
(
!
(
fc
->
oformat
->
flags
&
AVFMT_NOFILE
))
avio_closep
(
&
fc
->
pb
);
avformat_free_context
(
fc
);
*
pfc
=
NULL
;
}
void
of_close
(
OutputFile
**
pof
)
void
of_close
(
OutputFile
**
pof
)
{
{
OutputFile
*
of
=
*
pof
;
OutputFile
*
of
=
*
pof
;
AVFormatContext
*
s
;
Muxer
*
mux
;
if
(
!
of
)
if
(
!
of
)
return
;
return
;
mux
=
mux_from_of
(
of
);
thread_stop
(
mux
);
s
=
of
->
ctx
;
sq_free
(
&
of
->
sq_encode
);
if
(
s
&&
s
->
oformat
&&
!
(
s
->
oformat
->
flags
&
AVFMT_NOFILE
))
sq_free
(
&
mux
->
sq_mux
);
avio_closep
(
&
s
->
pb
);
avformat_free_context
(
s
);
for
(
int
i
=
0
;
i
<
of
->
nb_streams
;
i
++
)
av_dict_free
(
&
of
->
opts
);
ost_free
(
&
of
->
streams
[
i
]);
av_freep
(
&
of
->
streams
);
av_dict_free
(
&
mux
->
opts
);
av_packet_free
(
&
mux
->
sq_pkt
);
fc_close
(
&
mux
->
fc
);
av_freep
(
pof
);
av_freep
(
pof
);
}
}
int64_t
of_filesize
(
OutputFile
*
of
)
{
Muxer
*
mux
=
mux_from_of
(
of
);
return
atomic_load
(
&
mux
->
last_filesize
);
}
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.h
0 → 100644
浏览文件 @
f23762b9
/*
* Muxer internal APIs - should not be included outside of ffmpeg_mux*
* Copyright (c) 2023 ARTHENICA LTD
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of ffmpeg_mux.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools header names updated
* - want_sdp made thread-local
* - EncStatsFile declaration migrated from ffmpeg_mux_init.c
* - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT, SPECIFIER_OPT_FMT declarations migrated from
* ffmpeg.h
* - ms_from_ost migrated to ffmpeg_mux.c
*/
#ifndef FFTOOLS_FFMPEG_MUX_H
#define FFTOOLS_FFMPEG_MUX_H
#include <stdatomic.h>
#include <stdint.h>
#include "fftools_thread_queue.h"
#include "libavformat/avformat.h"
#include "libavcodec/packet.h"
#include "libavutil/dict.h"
#include "libavutil/fifo.h"
#include "libavutil/thread.h"
#define SPECIFIER_OPT_FMT_str "%s"
#define SPECIFIER_OPT_FMT_i "%i"
#define SPECIFIER_OPT_FMT_i64 "%"PRId64
#define SPECIFIER_OPT_FMT_ui64 "%"PRIu64
#define SPECIFIER_OPT_FMT_f "%f"
#define SPECIFIER_OPT_FMT_dbl "%lf"
#define WARN_MULTIPLE_OPT_USAGE(name, type, so, st)\
{\
char namestr[128] = "";\
const char *spec = so->specifier && so->specifier[0] ? so->specifier : "";\
for (int _i = 0; opt_name_##name[_i]; _i++)\
av_strlcatf(namestr, sizeof(namestr), "-%s%s", opt_name_##name[_i], opt_name_##name[_i+1] ? (opt_name_##name[_i+2] ? ", " : " or ") : "");\
av_log(NULL, AV_LOG_WARNING, "Multiple %s options specified for stream %d, only the last option '-%s%s%s "SPECIFIER_OPT_FMT_##type"' will be used.\n",\
namestr, st->index, opt_name_##name[0], spec[0] ? ":" : "", spec, so->u.type);\
}
#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
{\
int _ret, _matches = 0;\
SpecifierOpt *so;\
for (int _i = 0; _i < o->nb_ ## name; _i++) {\
char *spec = o->name[_i].specifier;\
if ((_ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\
outvar = o->name[_i].u.type;\
so = &o->name[_i];\
_matches++;\
} else if (_ret < 0)\
exit_program(1);\
}\
if (_matches > 1)\
WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\
}
#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\
{\
int i;\
for (i = 0; i < o->nb_ ## name; i++) {\
char *spec = o->name[i].specifier;\
if (!strcmp(spec, mediatype))\
outvar = o->name[i].u.type;\
}\
}
typedef
struct
MuxStream
{
OutputStream
ost
;
// name used for logging
char
log_name
[
32
];
/* the packets are buffered here until the muxer is ready to be initialized */
AVFifo
*
muxing_queue
;
AVBSFContext
*
bsf_ctx
;
EncStats
stats
;
int64_t
max_frames
;
/*
* The size of the AVPackets' buffers in queue.
* Updated when a packet is either pushed or pulled from the queue.
*/
size_t
muxing_queue_data_size
;
int
max_muxing_queue_size
;
/* Threshold after which max_muxing_queue_size will be in effect */
size_t
muxing_queue_data_threshold
;
/* dts of the last packet sent to the muxer, in the stream timebase
* used for making up missing dts values */
int64_t
last_mux_dts
;
}
MuxStream
;
typedef
struct
Muxer
{
OutputFile
of
;
// name used for logging
char
log_name
[
32
];
AVFormatContext
*
fc
;
pthread_t
thread
;
ThreadQueue
*
tq
;
AVDictionary
*
opts
;
int
thread_queue_size
;
/* filesize limit expressed in bytes */
int64_t
limit_filesize
;
atomic_int_least64_t
last_filesize
;
int
header_written
;
SyncQueue
*
sq_mux
;
AVPacket
*
sq_pkt
;
}
Muxer
;
typedef
struct
EncStatsFile
{
char
*
path
;
AVIOContext
*
io
;
}
EncStatsFile
;
/* whether we want to print an SDP, set in of_open() */
extern
__thread
int
want_sdp
;
int
mux_check_init
(
Muxer
*
mux
);
#endif
/* FFTOOLS_FFMPEG_MUX_H */
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux_init.c
0 → 100644
浏览文件 @
f23762b9
/*
* Muxer/output file setup.
* Copyright (c) 2023 ARTHENICA LTD
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of ffmpeg_mux_init.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools header names updated
* - EncStatsFile declaration migrated to ffmpeg_mux.h
* - extern ms_from_ost added
* - "class" member field renamed as clazz
*/
#include <string.h>
#include "fftools_cmdutils.h"
#include "fftools_ffmpeg.h"
#include "fftools_ffmpeg_mux.h"
#include "fftools_fopen_utf8.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/avutil.h"
#include "libavutil/bprint.h"
#include "libavutil/dict.h"
#include "libavutil/getenv_utf8.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass"
static
const
char
*
const
opt_name_apad
[]
=
{
"apad"
,
NULL
};
static
const
char
*
const
opt_name_autoscale
[]
=
{
"autoscale"
,
NULL
};
static
const
char
*
const
opt_name_bits_per_raw_sample
[]
=
{
"bits_per_raw_sample"
,
NULL
};
static
const
char
*
const
opt_name_bitstream_filters
[]
=
{
"bsf"
,
"absf"
,
"vbsf"
,
NULL
};
static
const
char
*
const
opt_name_copy_initial_nonkeyframes
[]
=
{
"copyinkf"
,
NULL
};
static
const
char
*
const
opt_name_copy_prior_start
[]
=
{
"copypriorss"
,
NULL
};
static
const
char
*
const
opt_name_disposition
[]
=
{
"disposition"
,
NULL
};
static
const
char
*
const
opt_name_enc_time_bases
[]
=
{
"enc_time_base"
,
NULL
};
static
const
char
*
const
opt_name_enc_stats_pre
[]
=
{
"enc_stats_pre"
,
NULL
};
static
const
char
*
const
opt_name_enc_stats_post
[]
=
{
"enc_stats_post"
,
NULL
};
static
const
char
*
const
opt_name_mux_stats
[]
=
{
"mux_stats"
,
NULL
};
static
const
char
*
const
opt_name_enc_stats_pre_fmt
[]
=
{
"enc_stats_pre_fmt"
,
NULL
};
static
const
char
*
const
opt_name_enc_stats_post_fmt
[]
=
{
"enc_stats_post_fmt"
,
NULL
};
static
const
char
*
const
opt_name_mux_stats_fmt
[]
=
{
"mux_stats_fmt"
,
NULL
};
static
const
char
*
const
opt_name_filters
[]
=
{
"filter"
,
"af"
,
"vf"
,
NULL
};
static
const
char
*
const
opt_name_filter_scripts
[]
=
{
"filter_script"
,
NULL
};
static
const
char
*
const
opt_name_fix_sub_duration_heartbeat
[]
=
{
"fix_sub_duration_heartbeat"
,
NULL
};
static
const
char
*
const
opt_name_fps_mode
[]
=
{
"fps_mode"
,
NULL
};
static
const
char
*
const
opt_name_force_fps
[]
=
{
"force_fps"
,
NULL
};
static
const
char
*
const
opt_name_forced_key_frames
[]
=
{
"forced_key_frames"
,
NULL
};
static
const
char
*
const
opt_name_frame_aspect_ratios
[]
=
{
"aspect"
,
NULL
};
static
const
char
*
const
opt_name_intra_matrices
[]
=
{
"intra_matrix"
,
NULL
};
static
const
char
*
const
opt_name_inter_matrices
[]
=
{
"inter_matrix"
,
NULL
};
static
const
char
*
const
opt_name_chroma_intra_matrices
[]
=
{
"chroma_intra_matrix"
,
NULL
};
static
const
char
*
const
opt_name_max_frame_rates
[]
=
{
"fpsmax"
,
NULL
};
static
const
char
*
const
opt_name_max_frames
[]
=
{
"frames"
,
"aframes"
,
"vframes"
,
"dframes"
,
NULL
};
static
const
char
*
const
opt_name_max_muxing_queue_size
[]
=
{
"max_muxing_queue_size"
,
NULL
};
static
const
char
*
const
opt_name_muxing_queue_data_threshold
[]
=
{
"muxing_queue_data_threshold"
,
NULL
};
static
const
char
*
const
opt_name_pass
[]
=
{
"pass"
,
NULL
};
static
const
char
*
const
opt_name_passlogfiles
[]
=
{
"passlogfile"
,
NULL
};
static
const
char
*
const
opt_name_presets
[]
=
{
"pre"
,
"apre"
,
"vpre"
,
"spre"
,
NULL
};
static
const
char
*
const
opt_name_qscale
[]
=
{
"q"
,
"qscale"
,
NULL
};
static
const
char
*
const
opt_name_rc_overrides
[]
=
{
"rc_override"
,
NULL
};
static
const
char
*
const
opt_name_time_bases
[]
=
{
"time_base"
,
NULL
};
static
const
char
*
const
opt_name_audio_channels
[]
=
{
"ac"
,
NULL
};
static
const
char
*
const
opt_name_audio_ch_layouts
[]
=
{
"channel_layout"
,
"ch_layout"
,
NULL
};
static
const
char
*
const
opt_name_audio_sample_rate
[]
=
{
"ar"
,
NULL
};
static
const
char
*
const
opt_name_frame_sizes
[]
=
{
"s"
,
NULL
};
static
const
char
*
const
opt_name_frame_pix_fmts
[]
=
{
"pix_fmt"
,
NULL
};
static
const
char
*
const
opt_name_sample_fmts
[]
=
{
"sample_fmt"
,
NULL
};
extern
MuxStream
*
ms_from_ost
(
OutputStream
*
ost
);
static
int
check_opt_bitexact
(
void
*
ctx
,
const
AVDictionary
*
opts
,
const
char
*
opt_name
,
int
flag
)
{
const
AVDictionaryEntry
*
e
=
av_dict_get
(
opts
,
opt_name
,
NULL
,
0
);
if
(
e
)
{
const
AVOption
*
o
=
av_opt_find
(
ctx
,
opt_name
,
NULL
,
0
,
0
);
int
val
=
0
;
if
(
!
o
)
return
0
;
av_opt_eval_flags
(
ctx
,
o
,
e
->
value
,
&
val
);
return
!!
(
val
&
flag
);
}
return
0
;
}
static
int
choose_encoder
(
const
OptionsContext
*
o
,
AVFormatContext
*
s
,
OutputStream
*
ost
,
const
AVCodec
**
enc
)
{
enum
AVMediaType
type
=
ost
->
st
->
codecpar
->
codec_type
;
char
*
codec_name
=
NULL
;
*
enc
=
NULL
;
if
(
type
==
AVMEDIA_TYPE_VIDEO
||
type
==
AVMEDIA_TYPE_AUDIO
||
type
==
AVMEDIA_TYPE_SUBTITLE
)
{
MATCH_PER_STREAM_OPT
(
codec_names
,
str
,
codec_name
,
s
,
ost
->
st
);
if
(
!
codec_name
)
{
ost
->
st
->
codecpar
->
codec_id
=
av_guess_codec
(
s
->
oformat
,
NULL
,
s
->
url
,
NULL
,
ost
->
st
->
codecpar
->
codec_type
);
*
enc
=
avcodec_find_encoder
(
ost
->
st
->
codecpar
->
codec_id
);
if
(
!*
enc
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Automatic encoder selection failed "
"Default encoder for format %s (codec %s) is "
"probably disabled. Please choose an encoder manually.
\n
"
,
s
->
oformat
->
name
,
avcodec_get_name
(
ost
->
st
->
codecpar
->
codec_id
));
return
AVERROR_ENCODER_NOT_FOUND
;
}
}
else
if
(
strcmp
(
codec_name
,
"copy"
))
{
*
enc
=
find_codec_or_die
(
ost
,
codec_name
,
ost
->
st
->
codecpar
->
codec_type
,
1
);
ost
->
st
->
codecpar
->
codec_id
=
(
*
enc
)
->
id
;
}
}
return
0
;
}
static
char
*
get_line
(
AVIOContext
*
s
,
AVBPrint
*
bprint
)
{
char
c
;
while
((
c
=
avio_r8
(
s
))
&&
c
!=
'\n'
)
av_bprint_chars
(
bprint
,
c
,
1
);
if
(
!
av_bprint_is_complete
(
bprint
))
report_and_exit
(
AVERROR
(
ENOMEM
));
return
bprint
->
str
;
}
static
int
get_preset_file_2
(
const
char
*
preset_name
,
const
char
*
codec_name
,
AVIOContext
**
s
)
{
int
i
,
ret
=
-
1
;
char
filename
[
1000
];
char
*
env_avconv_datadir
=
getenv_utf8
(
"AVCONV_DATADIR"
);
char
*
env_home
=
getenv_utf8
(
"HOME"
);
const
char
*
base
[
3
]
=
{
env_avconv_datadir
,
env_home
,
AVCONV_DATADIR
,
};
for
(
i
=
0
;
i
<
FF_ARRAY_ELEMS
(
base
)
&&
ret
<
0
;
i
++
)
{
if
(
!
base
[
i
])
continue
;
if
(
codec_name
)
{
snprintf
(
filename
,
sizeof
(
filename
),
"%s%s/%s-%s.avpreset"
,
base
[
i
],
i
!=
1
?
""
:
"/.avconv"
,
codec_name
,
preset_name
);
ret
=
avio_open2
(
s
,
filename
,
AVIO_FLAG_READ
,
&
int_cb
,
NULL
);
}
if
(
ret
<
0
)
{
snprintf
(
filename
,
sizeof
(
filename
),
"%s%s/%s.avpreset"
,
base
[
i
],
i
!=
1
?
""
:
"/.avconv"
,
preset_name
);
ret
=
avio_open2
(
s
,
filename
,
AVIO_FLAG_READ
,
&
int_cb
,
NULL
);
}
}
freeenv_utf8
(
env_home
);
freeenv_utf8
(
env_avconv_datadir
);
return
ret
;
}
__thread
EncStatsFile
*
enc_stats_files
;
__thread
int
nb_enc_stats_files
;
static
int
enc_stats_get_file
(
AVIOContext
**
io
,
const
char
*
path
)
{
EncStatsFile
*
esf
;
int
ret
;
for
(
int
i
=
0
;
i
<
nb_enc_stats_files
;
i
++
)
if
(
!
strcmp
(
path
,
enc_stats_files
[
i
].
path
))
{
*
io
=
enc_stats_files
[
i
].
io
;
return
0
;
}
GROW_ARRAY
(
enc_stats_files
,
nb_enc_stats_files
);
esf
=
&
enc_stats_files
[
nb_enc_stats_files
-
1
];
ret
=
avio_open2
(
&
esf
->
io
,
path
,
AVIO_FLAG_WRITE
,
&
int_cb
,
NULL
);
if
(
ret
<
0
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error opening stats file '%s': %s
\n
"
,
path
,
av_err2str
(
ret
));
return
ret
;
}
esf
->
path
=
av_strdup
(
path
);
if
(
!
esf
->
path
)
return
AVERROR
(
ENOMEM
);
*
io
=
esf
->
io
;
return
0
;
}
void
of_enc_stats_close
(
void
)
{
for
(
int
i
=
0
;
i
<
nb_enc_stats_files
;
i
++
)
{
av_freep
(
&
enc_stats_files
[
i
].
path
);
avio_closep
(
&
enc_stats_files
[
i
].
io
);
}
av_freep
(
&
enc_stats_files
);
nb_enc_stats_files
=
0
;
}
static
int
unescape
(
char
**
pdst
,
size_t
*
dst_len
,
const
char
**
pstr
,
char
delim
)
{
const
char
*
str
=
*
pstr
;
char
*
dst
;
size_t
len
,
idx
;
*
pdst
=
NULL
;
len
=
strlen
(
str
);
if
(
!
len
)
return
0
;
dst
=
av_malloc
(
len
+
1
);
if
(
!
dst
)
return
AVERROR
(
ENOMEM
);
for
(
idx
=
0
;
*
str
;
idx
++
,
str
++
)
{
if
(
str
[
0
]
==
'\\'
&&
str
[
1
])
str
++
;
else
if
(
*
str
==
delim
)
break
;
dst
[
idx
]
=
*
str
;
}
if
(
!
idx
)
{
av_freep
(
&
dst
);
return
0
;
}
dst
[
idx
]
=
0
;
*
pdst
=
dst
;
*
dst_len
=
idx
;
*
pstr
=
str
;
return
0
;
}
static
int
enc_stats_init
(
OutputStream
*
ost
,
EncStats
*
es
,
int
pre
,
const
char
*
path
,
const
char
*
fmt_spec
)
{
static
const
struct
{
enum
EncStatsType
type
;
const
char
*
str
;
int
pre_only
:
1
;
int
post_only
:
1
;
int
need_input_data
:
1
;
}
fmt_specs
[]
=
{
{
ENC_STATS_FILE_IDX
,
"fidx"
},
{
ENC_STATS_STREAM_IDX
,
"sidx"
},
{
ENC_STATS_FRAME_NUM
,
"n"
},
{
ENC_STATS_FRAME_NUM_IN
,
"ni"
,
0
,
0
,
1
},
{
ENC_STATS_TIMEBASE
,
"tb"
},
{
ENC_STATS_TIMEBASE_IN
,
"tbi"
,
0
,
0
,
1
},
{
ENC_STATS_PTS
,
"pts"
},
{
ENC_STATS_PTS_TIME
,
"t"
},
{
ENC_STATS_PTS_IN
,
"ptsi"
,
0
,
0
,
1
},
{
ENC_STATS_PTS_TIME_IN
,
"ti"
,
0
,
0
,
1
},
{
ENC_STATS_DTS
,
"dts"
,
0
,
1
},
{
ENC_STATS_DTS_TIME
,
"dt"
,
0
,
1
},
{
ENC_STATS_SAMPLE_NUM
,
"sn"
,
1
},
{
ENC_STATS_NB_SAMPLES
,
"samp"
,
1
},
{
ENC_STATS_PKT_SIZE
,
"size"
,
0
,
1
},
{
ENC_STATS_BITRATE
,
"br"
,
0
,
1
},
{
ENC_STATS_AVG_BITRATE
,
"abr"
,
0
,
1
},
};
const
char
*
next
=
fmt_spec
;
int
ret
;
while
(
*
next
)
{
EncStatsComponent
*
c
;
char
*
val
;
size_t
val_len
;
// get the sequence up until next opening brace
ret
=
unescape
(
&
val
,
&
val_len
,
&
next
,
'{'
);
if
(
ret
<
0
)
return
ret
;
if
(
val
)
{
GROW_ARRAY
(
es
->
components
,
es
->
nb_components
);
c
=
&
es
->
components
[
es
->
nb_components
-
1
];
c
->
type
=
ENC_STATS_LITERAL
;
c
->
str
=
val
;
c
->
str_len
=
val_len
;
}
if
(
!*
next
)
break
;
next
++
;
// get the part inside braces
ret
=
unescape
(
&
val
,
&
val_len
,
&
next
,
'}'
);
if
(
ret
<
0
)
return
ret
;
if
(
!
val
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Empty formatting directive in: %s
\n
"
,
fmt_spec
);
return
AVERROR
(
EINVAL
);
}
if
(
!*
next
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Missing closing brace in: %s
\n
"
,
fmt_spec
);
ret
=
AVERROR
(
EINVAL
);
goto
fail
;
}
next
++
;
GROW_ARRAY
(
es
->
components
,
es
->
nb_components
);
c
=
&
es
->
components
[
es
->
nb_components
-
1
];
for
(
size_t
i
=
0
;
i
<
FF_ARRAY_ELEMS
(
fmt_specs
);
i
++
)
{
if
(
!
strcmp
(
val
,
fmt_specs
[
i
].
str
))
{
if
((
pre
&&
fmt_specs
[
i
].
post_only
)
||
(
!
pre
&&
fmt_specs
[
i
].
pre_only
))
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Format directive '%s' may only be used %s-encoding
\n
"
,
val
,
pre
?
"post"
:
"pre"
);
ret
=
AVERROR
(
EINVAL
);
goto
fail
;
}
c
->
type
=
fmt_specs
[
i
].
type
;
if
(
fmt_specs
[
i
].
need_input_data
)
{
if
(
ost
->
ist
)
ost
->
ist
->
want_frame_data
=
1
;
else
{
av_log
(
ost
,
AV_LOG_WARNING
,
"Format directive '%s' is unavailable, because "
"this output stream has no associated input stream
\n
"
,
val
);
}
}
break
;
}
}
if
(
!
c
->
type
)
{
av_log
(
NULL
,
AV_LOG_ERROR
,
"Invalid format directive: %s
\n
"
,
val
);
ret
=
AVERROR
(
EINVAL
);
goto
fail
;
}
fail:
av_freep
(
&
val
);
if
(
ret
<
0
)
return
ret
;
}
ret
=
enc_stats_get_file
(
&
es
->
io
,
path
);
if
(
ret
<
0
)
return
ret
;
return
0
;
}
static
const
char
*
output_stream_item_name
(
void
*
obj
)
{
const
MuxStream
*
ms
=
obj
;
return
ms
->
log_name
;
}
static
const
AVClass
output_stream_class
=
{
.
class_name
=
"OutputStream"
,
.
version
=
LIBAVUTIL_VERSION_INT
,
.
item_name
=
output_stream_item_name
,
.
category
=
AV_CLASS_CATEGORY_MUXER
,
};
static
MuxStream
*
mux_stream_alloc
(
Muxer
*
mux
,
enum
AVMediaType
type
)
{
const
char
*
type_str
=
av_get_media_type_string
(
type
);
MuxStream
*
ms
=
allocate_array_elem
(
&
mux
->
of
.
streams
,
sizeof
(
*
ms
),
&
mux
->
of
.
nb_streams
);
ms
->
ost
.
file_index
=
mux
->
of
.
index
;
ms
->
ost
.
index
=
mux
->
of
.
nb_streams
-
1
;
ms
->
ost
.
clazz
=
&
output_stream_class
;
snprintf
(
ms
->
log_name
,
sizeof
(
ms
->
log_name
),
"%cost#%d:%d"
,
type_str
?
*
type_str
:
'?'
,
mux
->
of
.
index
,
ms
->
ost
.
index
);
return
ms
;
}
static
OutputStream
*
new_output_stream
(
Muxer
*
mux
,
const
OptionsContext
*
o
,
enum
AVMediaType
type
,
InputStream
*
ist
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
MuxStream
*
ms
;
OutputStream
*
ost
;
const
AVCodec
*
enc
;
AVStream
*
st
=
avformat_new_stream
(
oc
,
NULL
);
int
ret
=
0
;
const
char
*
bsfs
=
NULL
,
*
time_base
=
NULL
;
char
*
next
,
*
codec_tag
=
NULL
;
double
qscale
=
-
1
;
int
i
;
if
(
!
st
)
report_and_exit
(
AVERROR
(
ENOMEM
));
if
(
oc
->
nb_streams
-
1
<
o
->
nb_streamid_map
)
st
->
id
=
o
->
streamid_map
[
oc
->
nb_streams
-
1
];
ms
=
mux_stream_alloc
(
mux
,
type
);
ost
=
&
ms
->
ost
;
ms
->
muxing_queue
=
av_fifo_alloc2
(
8
,
sizeof
(
AVPacket
*
),
0
);
if
(
!
ms
->
muxing_queue
)
report_and_exit
(
AVERROR
(
ENOMEM
));
ms
->
last_mux_dts
=
AV_NOPTS_VALUE
;
ost
->
st
=
st
;
ost
->
ist
=
ist
;
ost
->
kf
.
ref_pts
=
AV_NOPTS_VALUE
;
st
->
codecpar
->
codec_type
=
type
;
ret
=
choose_encoder
(
o
,
oc
,
ost
,
&
enc
);
if
(
ret
<
0
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Error selecting an encoder
\n
"
);
exit_program
(
1
);
}
if
(
enc
)
{
ost
->
enc_ctx
=
avcodec_alloc_context3
(
enc
);
if
(
!
ost
->
enc_ctx
)
report_and_exit
(
AVERROR
(
ENOMEM
));
av_strlcat
(
ms
->
log_name
,
"/"
,
sizeof
(
ms
->
log_name
));
av_strlcat
(
ms
->
log_name
,
enc
->
name
,
sizeof
(
ms
->
log_name
));
}
else
{
av_strlcat
(
ms
->
log_name
,
"/copy"
,
sizeof
(
ms
->
log_name
));
}
ost
->
filtered_frame
=
av_frame_alloc
();
if
(
!
ost
->
filtered_frame
)
report_and_exit
(
AVERROR
(
ENOMEM
));
ost
->
pkt
=
av_packet_alloc
();
if
(
!
ost
->
pkt
)
report_and_exit
(
AVERROR
(
ENOMEM
));
if
(
ost
->
enc_ctx
)
{
AVCodecContext
*
enc
=
ost
->
enc_ctx
;
AVIOContext
*
s
=
NULL
;
char
*
buf
=
NULL
,
*
arg
=
NULL
,
*
preset
=
NULL
;
const
char
*
enc_stats_pre
=
NULL
,
*
enc_stats_post
=
NULL
,
*
mux_stats
=
NULL
;
ost
->
encoder_opts
=
filter_codec_opts
(
o
->
g
->
codec_opts
,
enc
->
codec_id
,
oc
,
st
,
enc
->
codec
);
MATCH_PER_STREAM_OPT
(
presets
,
str
,
preset
,
oc
,
st
);
ost
->
autoscale
=
1
;
MATCH_PER_STREAM_OPT
(
autoscale
,
i
,
ost
->
autoscale
,
oc
,
st
);
if
(
preset
&&
(
!
(
ret
=
get_preset_file_2
(
preset
,
enc
->
codec
->
name
,
&
s
))))
{
AVBPrint
bprint
;
av_bprint_init
(
&
bprint
,
0
,
AV_BPRINT_SIZE_UNLIMITED
);
do
{
av_bprint_clear
(
&
bprint
);
buf
=
get_line
(
s
,
&
bprint
);
if
(
!
buf
[
0
]
||
buf
[
0
]
==
'#'
)
continue
;
if
(
!
(
arg
=
strchr
(
buf
,
'='
)))
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid line found in the preset file.
\n
"
);
exit_program
(
1
);
}
*
arg
++
=
0
;
av_dict_set
(
&
ost
->
encoder_opts
,
buf
,
arg
,
AV_DICT_DONT_OVERWRITE
);
}
while
(
!
s
->
eof_reached
);
av_bprint_finalize
(
&
bprint
,
NULL
);
avio_closep
(
&
s
);
}
if
(
ret
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Preset %s specified, but could not be opened.
\n
"
,
preset
);
exit_program
(
1
);
}
MATCH_PER_STREAM_OPT
(
enc_stats_pre
,
str
,
enc_stats_pre
,
oc
,
st
);
if
(
enc_stats_pre
&&
(
type
==
AVMEDIA_TYPE_VIDEO
||
type
==
AVMEDIA_TYPE_AUDIO
))
{
const
char
*
format
=
"{fidx} {sidx} {n} {t}"
;
MATCH_PER_STREAM_OPT
(
enc_stats_pre_fmt
,
str
,
format
,
oc
,
st
);
ret
=
enc_stats_init
(
ost
,
&
ost
->
enc_stats_pre
,
1
,
enc_stats_pre
,
format
);
if
(
ret
<
0
)
exit_program
(
1
);
}
MATCH_PER_STREAM_OPT
(
enc_stats_post
,
str
,
enc_stats_post
,
oc
,
st
);
if
(
enc_stats_post
&&
(
type
==
AVMEDIA_TYPE_VIDEO
||
type
==
AVMEDIA_TYPE_AUDIO
))
{
const
char
*
format
=
"{fidx} {sidx} {n} {t}"
;
MATCH_PER_STREAM_OPT
(
enc_stats_post_fmt
,
str
,
format
,
oc
,
st
);
ret
=
enc_stats_init
(
ost
,
&
ost
->
enc_stats_post
,
0
,
enc_stats_post
,
format
);
if
(
ret
<
0
)
exit_program
(
1
);
}
MATCH_PER_STREAM_OPT
(
mux_stats
,
str
,
mux_stats
,
oc
,
st
);
if
(
mux_stats
&&
(
type
==
AVMEDIA_TYPE_VIDEO
||
type
==
AVMEDIA_TYPE_AUDIO
))
{
const
char
*
format
=
"{fidx} {sidx} {n} {t}"
;
MATCH_PER_STREAM_OPT
(
mux_stats_fmt
,
str
,
format
,
oc
,
st
);
ret
=
enc_stats_init
(
ost
,
&
ms
->
stats
,
0
,
mux_stats
,
format
);
if
(
ret
<
0
)
exit_program
(
1
);
}
}
else
{
ost
->
encoder_opts
=
filter_codec_opts
(
o
->
g
->
codec_opts
,
AV_CODEC_ID_NONE
,
oc
,
st
,
NULL
);
}
if
(
o
->
bitexact
)
{
ost
->
bitexact
=
1
;
}
else
if
(
ost
->
enc_ctx
)
{
ost
->
bitexact
=
check_opt_bitexact
(
ost
->
enc_ctx
,
ost
->
encoder_opts
,
"flags"
,
AV_CODEC_FLAG_BITEXACT
);
}
MATCH_PER_STREAM_OPT
(
time_bases
,
str
,
time_base
,
oc
,
st
);
if
(
time_base
)
{
AVRational
q
;
if
(
av_parse_ratio
(
&
q
,
time_base
,
INT_MAX
,
0
,
NULL
)
<
0
||
q
.
num
<=
0
||
q
.
den
<=
0
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid time base: %s
\n
"
,
time_base
);
exit_program
(
1
);
}
st
->
time_base
=
q
;
}
MATCH_PER_STREAM_OPT
(
enc_time_bases
,
str
,
time_base
,
oc
,
st
);
if
(
time_base
)
{
AVRational
q
;
if
(
av_parse_ratio
(
&
q
,
time_base
,
INT_MAX
,
0
,
NULL
)
<
0
||
q
.
den
<=
0
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid time base: %s
\n
"
,
time_base
);
exit_program
(
1
);
}
ost
->
enc_timebase
=
q
;
}
ms
->
max_frames
=
INT64_MAX
;
MATCH_PER_STREAM_OPT
(
max_frames
,
i64
,
ms
->
max_frames
,
oc
,
st
);
for
(
i
=
0
;
i
<
o
->
nb_max_frames
;
i
++
)
{
char
*
p
=
o
->
max_frames
[
i
].
specifier
;
if
(
!*
p
&&
type
!=
AVMEDIA_TYPE_VIDEO
)
{
av_log
(
ost
,
AV_LOG_WARNING
,
"Applying unspecific -frames to non video streams, maybe you meant -vframes ?
\n
"
);
break
;
}
}
ost
->
copy_prior_start
=
-
1
;
MATCH_PER_STREAM_OPT
(
copy_prior_start
,
i
,
ost
->
copy_prior_start
,
oc
,
st
);
MATCH_PER_STREAM_OPT
(
bitstream_filters
,
str
,
bsfs
,
oc
,
st
);
if
(
bsfs
&&
*
bsfs
)
{
ret
=
av_bsf_list_parse_str
(
bsfs
,
&
ms
->
bsf_ctx
);
if
(
ret
<
0
)
{
av_log
(
ost
,
AV_LOG_ERROR
,
"Error parsing bitstream filter sequence '%s': %s
\n
"
,
bsfs
,
av_err2str
(
ret
));
exit_program
(
1
);
}
}
MATCH_PER_STREAM_OPT
(
codec_tags
,
str
,
codec_tag
,
oc
,
st
);
if
(
codec_tag
)
{
uint32_t
tag
=
strtol
(
codec_tag
,
&
next
,
0
);
if
(
*
next
)
tag
=
AV_RL32
(
codec_tag
);
ost
->
st
->
codecpar
->
codec_tag
=
tag
;
if
(
ost
->
enc_ctx
)
ost
->
enc_ctx
->
codec_tag
=
tag
;
}
MATCH_PER_STREAM_OPT
(
qscale
,
dbl
,
qscale
,
oc
,
st
);
if
(
ost
->
enc_ctx
&&
qscale
>=
0
)
{
ost
->
enc_ctx
->
flags
|=
AV_CODEC_FLAG_QSCALE
;
ost
->
enc_ctx
->
global_quality
=
FF_QP2LAMBDA
*
qscale
;
}
ms
->
max_muxing_queue_size
=
128
;
MATCH_PER_STREAM_OPT
(
max_muxing_queue_size
,
i
,
ms
->
max_muxing_queue_size
,
oc
,
st
);
ms
->
muxing_queue_data_threshold
=
50
*
1024
*
1024
;
MATCH_PER_STREAM_OPT
(
muxing_queue_data_threshold
,
i
,
ms
->
muxing_queue_data_threshold
,
oc
,
st
);
MATCH_PER_STREAM_OPT
(
bits_per_raw_sample
,
i
,
ost
->
bits_per_raw_sample
,
oc
,
st
);
MATCH_PER_STREAM_OPT
(
fix_sub_duration_heartbeat
,
i
,
ost
->
fix_sub_duration_heartbeat
,
oc
,
st
);
if
(
oc
->
oformat
->
flags
&
AVFMT_GLOBALHEADER
&&
ost
->
enc_ctx
)
ost
->
enc_ctx
->
flags
|=
AV_CODEC_FLAG_GLOBAL_HEADER
;
av_dict_copy
(
&
ost
->
sws_dict
,
o
->
g
->
sws_dict
,
0
);
av_dict_copy
(
&
ost
->
swr_opts
,
o
->
g
->
swr_opts
,
0
);
if
(
ost
->
enc_ctx
&&
av_get_exact_bits_per_sample
(
ost
->
enc_ctx
->
codec_id
)
==
24
)
av_dict_set
(
&
ost
->
swr_opts
,
"output_sample_bits"
,
"24"
,
0
);
if
(
ost
->
ist
)
{
ost
->
ist
->
discard
=
0
;
ost
->
ist
->
st
->
discard
=
ost
->
ist
->
user_set_discard
;
}
ost
->
last_mux_dts
=
AV_NOPTS_VALUE
;
ost
->
last_filter_pts
=
AV_NOPTS_VALUE
;
MATCH_PER_STREAM_OPT
(
copy_initial_nonkeyframes
,
i
,
ost
->
copy_initial_nonkeyframes
,
oc
,
st
);
return
ost
;
}
static
char
*
get_ost_filters
(
const
OptionsContext
*
o
,
AVFormatContext
*
oc
,
OutputStream
*
ost
)
{
AVStream
*
st
=
ost
->
st
;
if
(
ost
->
filters_script
&&
ost
->
filters
)
{
av_log
(
ost
,
AV_LOG_ERROR
,
"Both -filter and -filter_script set
\n
"
);
exit_program
(
1
);
}
if
(
ost
->
filters_script
)
return
file_read
(
ost
->
filters_script
);
else
if
(
ost
->
filters
)
return
av_strdup
(
ost
->
filters
);
return
av_strdup
(
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
?
"null"
:
"anull"
);
}
static
void
check_streamcopy_filters
(
const
OptionsContext
*
o
,
AVFormatContext
*
oc
,
OutputStream
*
ost
,
enum
AVMediaType
type
)
{
if
(
ost
->
filters_script
||
ost
->
filters
)
{
av_log
(
ost
,
AV_LOG_ERROR
,
"%s '%s' was defined, but codec copy was selected.
\n
"
"Filtering and streamcopy cannot be used together.
\n
"
,
ost
->
filters
?
"Filtergraph"
:
"Filtergraph script"
,
ost
->
filters
?
ost
->
filters
:
ost
->
filters_script
);
exit_program
(
1
);
}
}
static
void
parse_matrix_coeffs
(
void
*
logctx
,
uint16_t
*
dest
,
const
char
*
str
)
{
int
i
;
const
char
*
p
=
str
;
for
(
i
=
0
;;
i
++
)
{
dest
[
i
]
=
atoi
(
p
);
if
(
i
==
63
)
break
;
p
=
strchr
(
p
,
','
);
if
(
!
p
)
{
av_log
(
logctx
,
AV_LOG_FATAL
,
"Syntax error in matrix
\"
%s
\"
at coeff %d
\n
"
,
str
,
i
);
exit_program
(
1
);
}
p
++
;
}
}
static
OutputStream
*
new_video_stream
(
Muxer
*
mux
,
const
OptionsContext
*
o
,
InputStream
*
ist
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
AVStream
*
st
;
OutputStream
*
ost
;
char
*
frame_rate
=
NULL
,
*
max_frame_rate
=
NULL
,
*
frame_aspect_ratio
=
NULL
;
ost
=
new_output_stream
(
mux
,
o
,
AVMEDIA_TYPE_VIDEO
,
ist
);
st
=
ost
->
st
;
MATCH_PER_STREAM_OPT
(
frame_rates
,
str
,
frame_rate
,
oc
,
st
);
if
(
frame_rate
&&
av_parse_video_rate
(
&
ost
->
frame_rate
,
frame_rate
)
<
0
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid framerate value: %s
\n
"
,
frame_rate
);
exit_program
(
1
);
}
MATCH_PER_STREAM_OPT
(
max_frame_rates
,
str
,
max_frame_rate
,
oc
,
st
);
if
(
max_frame_rate
&&
av_parse_video_rate
(
&
ost
->
max_frame_rate
,
max_frame_rate
)
<
0
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid maximum framerate value: %s
\n
"
,
max_frame_rate
);
exit_program
(
1
);
}
if
(
frame_rate
&&
max_frame_rate
)
{
av_log
(
ost
,
AV_LOG_ERROR
,
"Only one of -fpsmax and -r can be set for a stream.
\n
"
);
exit_program
(
1
);
}
MATCH_PER_STREAM_OPT
(
frame_aspect_ratios
,
str
,
frame_aspect_ratio
,
oc
,
st
);
if
(
frame_aspect_ratio
)
{
AVRational
q
;
if
(
av_parse_ratio
(
&
q
,
frame_aspect_ratio
,
255
,
0
,
NULL
)
<
0
||
q
.
num
<=
0
||
q
.
den
<=
0
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid aspect ratio: %s
\n
"
,
frame_aspect_ratio
);
exit_program
(
1
);
}
ost
->
frame_aspect_ratio
=
q
;
}
MATCH_PER_STREAM_OPT
(
filter_scripts
,
str
,
ost
->
filters_script
,
oc
,
st
);
MATCH_PER_STREAM_OPT
(
filters
,
str
,
ost
->
filters
,
oc
,
st
);
if
(
ost
->
enc_ctx
)
{
AVCodecContext
*
video_enc
=
ost
->
enc_ctx
;
const
char
*
p
=
NULL
,
*
fps_mode
=
NULL
;
char
*
frame_size
=
NULL
;
char
*
frame_pix_fmt
=
NULL
;
char
*
intra_matrix
=
NULL
,
*
inter_matrix
=
NULL
;
char
*
chroma_intra_matrix
=
NULL
;
int
do_pass
=
0
;
int
i
;
MATCH_PER_STREAM_OPT
(
frame_sizes
,
str
,
frame_size
,
oc
,
st
);
if
(
frame_size
&&
av_parse_video_size
(
&
video_enc
->
width
,
&
video_enc
->
height
,
frame_size
)
<
0
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid frame size: %s.
\n
"
,
frame_size
);
exit_program
(
1
);
}
MATCH_PER_STREAM_OPT
(
frame_pix_fmts
,
str
,
frame_pix_fmt
,
oc
,
st
);
if
(
frame_pix_fmt
&&
*
frame_pix_fmt
==
'+'
)
{
ost
->
keep_pix_fmt
=
1
;
if
(
!*++
frame_pix_fmt
)
frame_pix_fmt
=
NULL
;
}
if
(
frame_pix_fmt
&&
(
video_enc
->
pix_fmt
=
av_get_pix_fmt
(
frame_pix_fmt
))
==
AV_PIX_FMT_NONE
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Unknown pixel format requested: %s.
\n
"
,
frame_pix_fmt
);
exit_program
(
1
);
}
st
->
sample_aspect_ratio
=
video_enc
->
sample_aspect_ratio
;
MATCH_PER_STREAM_OPT
(
intra_matrices
,
str
,
intra_matrix
,
oc
,
st
);
if
(
intra_matrix
)
{
if
(
!
(
video_enc
->
intra_matrix
=
av_mallocz
(
sizeof
(
*
video_enc
->
intra_matrix
)
*
64
)))
report_and_exit
(
AVERROR
(
ENOMEM
));
parse_matrix_coeffs
(
ost
,
video_enc
->
intra_matrix
,
intra_matrix
);
}
MATCH_PER_STREAM_OPT
(
chroma_intra_matrices
,
str
,
chroma_intra_matrix
,
oc
,
st
);
if
(
chroma_intra_matrix
)
{
uint16_t
*
p
=
av_mallocz
(
sizeof
(
*
video_enc
->
chroma_intra_matrix
)
*
64
);
if
(
!
p
)
report_and_exit
(
AVERROR
(
ENOMEM
));
video_enc
->
chroma_intra_matrix
=
p
;
parse_matrix_coeffs
(
ost
,
p
,
chroma_intra_matrix
);
}
MATCH_PER_STREAM_OPT
(
inter_matrices
,
str
,
inter_matrix
,
oc
,
st
);
if
(
inter_matrix
)
{
if
(
!
(
video_enc
->
inter_matrix
=
av_mallocz
(
sizeof
(
*
video_enc
->
inter_matrix
)
*
64
)))
report_and_exit
(
AVERROR
(
ENOMEM
));
parse_matrix_coeffs
(
ost
,
video_enc
->
inter_matrix
,
inter_matrix
);
}
MATCH_PER_STREAM_OPT
(
rc_overrides
,
str
,
p
,
oc
,
st
);
for
(
i
=
0
;
p
;
i
++
)
{
int
start
,
end
,
q
;
int
e
=
sscanf
(
p
,
"%d,%d,%d"
,
&
start
,
&
end
,
&
q
);
if
(
e
!=
3
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"error parsing rc_override
\n
"
);
exit_program
(
1
);
}
video_enc
->
rc_override
=
av_realloc_array
(
video_enc
->
rc_override
,
i
+
1
,
sizeof
(
RcOverride
));
if
(
!
video_enc
->
rc_override
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Could not (re)allocate memory for rc_override.
\n
"
);
exit_program
(
1
);
}
video_enc
->
rc_override
[
i
].
start_frame
=
start
;
video_enc
->
rc_override
[
i
].
end_frame
=
end
;
if
(
q
>
0
)
{
video_enc
->
rc_override
[
i
].
qscale
=
q
;
video_enc
->
rc_override
[
i
].
quality_factor
=
1
.
0
;
}
else
{
video_enc
->
rc_override
[
i
].
qscale
=
0
;
video_enc
->
rc_override
[
i
].
quality_factor
=
-
q
/
100
.
0
;
}
p
=
strchr
(
p
,
'/'
);
if
(
p
)
p
++
;
}
video_enc
->
rc_override_count
=
i
;
#if FFMPEG_OPT_PSNR
if
(
do_psnr
)
{
av_log
(
ost
,
AV_LOG_WARNING
,
"The -psnr option is deprecated, use -flags +psnr
\n
"
);
video_enc
->
flags
|=
AV_CODEC_FLAG_PSNR
;
}
#endif
/* two pass mode */
MATCH_PER_STREAM_OPT
(
pass
,
i
,
do_pass
,
oc
,
st
);
if
(
do_pass
)
{
if
(
do_pass
&
1
)
{
video_enc
->
flags
|=
AV_CODEC_FLAG_PASS1
;
av_dict_set
(
&
ost
->
encoder_opts
,
"flags"
,
"+pass1"
,
AV_DICT_APPEND
);
}
if
(
do_pass
&
2
)
{
video_enc
->
flags
|=
AV_CODEC_FLAG_PASS2
;
av_dict_set
(
&
ost
->
encoder_opts
,
"flags"
,
"+pass2"
,
AV_DICT_APPEND
);
}
}
MATCH_PER_STREAM_OPT
(
passlogfiles
,
str
,
ost
->
logfile_prefix
,
oc
,
st
);
if
(
ost
->
logfile_prefix
&&
!
(
ost
->
logfile_prefix
=
av_strdup
(
ost
->
logfile_prefix
)))
report_and_exit
(
AVERROR
(
ENOMEM
));
if
(
do_pass
)
{
int
ost_idx
=
-
1
;
char
logfilename
[
1024
];
FILE
*
f
;
/* compute this stream's global index */
for
(
int
i
=
0
;
i
<=
ost
->
file_index
;
i
++
)
ost_idx
+=
output_files
[
i
]
->
nb_streams
;
snprintf
(
logfilename
,
sizeof
(
logfilename
),
"%s-%d.log"
,
ost
->
logfile_prefix
?
ost
->
logfile_prefix
:
DEFAULT_PASS_LOGFILENAME_PREFIX
,
ost_idx
);
if
(
!
strcmp
(
ost
->
enc_ctx
->
codec
->
name
,
"libx264"
))
{
av_dict_set
(
&
ost
->
encoder_opts
,
"stats"
,
logfilename
,
AV_DICT_DONT_OVERWRITE
);
}
else
{
if
(
video_enc
->
flags
&
AV_CODEC_FLAG_PASS2
)
{
char
*
logbuffer
=
file_read
(
logfilename
);
if
(
!
logbuffer
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Error reading log file '%s' for pass-2 encoding
\n
"
,
logfilename
);
exit_program
(
1
);
}
video_enc
->
stats_in
=
logbuffer
;
}
if
(
video_enc
->
flags
&
AV_CODEC_FLAG_PASS1
)
{
f
=
fopen_utf8
(
logfilename
,
"wb"
);
if
(
!
f
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Cannot write log file '%s' for pass-1 encoding: %s
\n
"
,
logfilename
,
strerror
(
errno
));
exit_program
(
1
);
}
ost
->
logfile
=
f
;
}
}
}
MATCH_PER_STREAM_OPT
(
force_fps
,
i
,
ost
->
force_fps
,
oc
,
st
);
ost
->
top_field_first
=
-
1
;
MATCH_PER_STREAM_OPT
(
top_field_first
,
i
,
ost
->
top_field_first
,
oc
,
st
);
ost
->
vsync_method
=
video_sync_method
;
MATCH_PER_STREAM_OPT
(
fps_mode
,
str
,
fps_mode
,
oc
,
st
);
if
(
fps_mode
)
parse_and_set_vsync
(
fps_mode
,
&
ost
->
vsync_method
,
ost
->
file_index
,
ost
->
index
,
0
);
if
((
ost
->
frame_rate
.
num
||
ost
->
max_frame_rate
.
num
)
&&
!
(
ost
->
vsync_method
==
VSYNC_AUTO
||
ost
->
vsync_method
==
VSYNC_CFR
||
ost
->
vsync_method
==
VSYNC_VSCFR
))
{
av_log
(
ost
,
AV_LOG_FATAL
,
"One of -r/-fpsmax was specified "
"together a non-CFR -vsync/-fps_mode. This is contradictory.
\n
"
);
exit_program
(
1
);
}
if
(
ost
->
vsync_method
==
VSYNC_AUTO
)
{
if
(
ost
->
frame_rate
.
num
||
ost
->
max_frame_rate
.
num
)
{
ost
->
vsync_method
=
VSYNC_CFR
;
}
else
if
(
!
strcmp
(
oc
->
oformat
->
name
,
"avi"
))
{
ost
->
vsync_method
=
VSYNC_VFR
;
}
else
{
ost
->
vsync_method
=
(
oc
->
oformat
->
flags
&
AVFMT_VARIABLE_FPS
)
?
((
oc
->
oformat
->
flags
&
AVFMT_NOTIMESTAMPS
)
?
VSYNC_PASSTHROUGH
:
VSYNC_VFR
)
:
VSYNC_CFR
;
}
if
(
ost
->
ist
&&
ost
->
vsync_method
==
VSYNC_CFR
)
{
const
InputFile
*
ifile
=
input_files
[
ost
->
ist
->
file_index
];
if
(
ifile
->
nb_streams
==
1
&&
ifile
->
input_ts_offset
==
0
)
ost
->
vsync_method
=
VSYNC_VSCFR
;
}
if
(
ost
->
vsync_method
==
VSYNC_CFR
&&
copy_ts
)
{
ost
->
vsync_method
=
VSYNC_VSCFR
;
}
}
ost
->
is_cfr
=
(
ost
->
vsync_method
==
VSYNC_CFR
||
ost
->
vsync_method
==
VSYNC_VSCFR
);
ost
->
avfilter
=
get_ost_filters
(
o
,
oc
,
ost
);
if
(
!
ost
->
avfilter
)
exit_program
(
1
);
ost
->
last_frame
=
av_frame_alloc
();
if
(
!
ost
->
last_frame
)
report_and_exit
(
AVERROR
(
ENOMEM
));
}
else
check_streamcopy_filters
(
o
,
oc
,
ost
,
AVMEDIA_TYPE_VIDEO
);
return
ost
;
}
static
OutputStream
*
new_audio_stream
(
Muxer
*
mux
,
const
OptionsContext
*
o
,
InputStream
*
ist
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
AVStream
*
st
;
OutputStream
*
ost
;
ost
=
new_output_stream
(
mux
,
o
,
AVMEDIA_TYPE_AUDIO
,
ist
);
st
=
ost
->
st
;
MATCH_PER_STREAM_OPT
(
filter_scripts
,
str
,
ost
->
filters_script
,
oc
,
st
);
MATCH_PER_STREAM_OPT
(
filters
,
str
,
ost
->
filters
,
oc
,
st
);
if
(
ost
->
enc_ctx
)
{
AVCodecContext
*
audio_enc
=
ost
->
enc_ctx
;
int
channels
=
0
;
char
*
layout
=
NULL
;
char
*
sample_fmt
=
NULL
;
MATCH_PER_STREAM_OPT
(
audio_channels
,
i
,
channels
,
oc
,
st
);
if
(
channels
)
{
audio_enc
->
ch_layout
.
order
=
AV_CHANNEL_ORDER_UNSPEC
;
audio_enc
->
ch_layout
.
nb_channels
=
channels
;
}
MATCH_PER_STREAM_OPT
(
audio_ch_layouts
,
str
,
layout
,
oc
,
st
);
if
(
layout
)
{
if
(
av_channel_layout_from_string
(
&
audio_enc
->
ch_layout
,
layout
)
<
0
)
{
#if FF_API_OLD_CHANNEL_LAYOUT
uint64_t
mask
;
AV_NOWARN_DEPRECATED
({
mask
=
av_get_channel_layout
(
layout
);
})
if
(
!
mask
)
{
#endif
av_log
(
ost
,
AV_LOG_FATAL
,
"Unknown channel layout: %s
\n
"
,
layout
);
exit_program
(
1
);
#if FF_API_OLD_CHANNEL_LAYOUT
}
av_log
(
ost
,
AV_LOG_WARNING
,
"Channel layout '%s' uses a deprecated syntax.
\n
"
,
layout
);
av_channel_layout_from_mask
(
&
audio_enc
->
ch_layout
,
mask
);
#endif
}
}
MATCH_PER_STREAM_OPT
(
sample_fmts
,
str
,
sample_fmt
,
oc
,
st
);
if
(
sample_fmt
&&
(
audio_enc
->
sample_fmt
=
av_get_sample_fmt
(
sample_fmt
))
==
AV_SAMPLE_FMT_NONE
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid sample format '%s'
\n
"
,
sample_fmt
);
exit_program
(
1
);
}
MATCH_PER_STREAM_OPT
(
audio_sample_rate
,
i
,
audio_enc
->
sample_rate
,
oc
,
st
);
MATCH_PER_STREAM_OPT
(
apad
,
str
,
ost
->
apad
,
oc
,
st
);
ost
->
apad
=
av_strdup
(
ost
->
apad
);
ost
->
avfilter
=
get_ost_filters
(
o
,
oc
,
ost
);
if
(
!
ost
->
avfilter
)
exit_program
(
1
);
#if FFMPEG_OPT_MAP_CHANNEL
/* check for channel mapping for this audio stream */
for
(
int
n
=
0
;
n
<
o
->
nb_audio_channel_maps
;
n
++
)
{
AudioChannelMap
*
map
=
&
o
->
audio_channel_maps
[
n
];
if
((
map
->
ofile_idx
==
-
1
||
ost
->
file_index
==
map
->
ofile_idx
)
&&
(
map
->
ostream_idx
==
-
1
||
ost
->
st
->
index
==
map
->
ostream_idx
))
{
InputStream
*
ist
;
if
(
map
->
channel_idx
==
-
1
)
{
ist
=
NULL
;
}
else
if
(
!
ost
->
ist
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Cannot determine input stream for channel mapping %d.%d
\n
"
,
ost
->
file_index
,
ost
->
st
->
index
);
continue
;
}
else
{
ist
=
ost
->
ist
;
}
if
(
!
ist
||
(
ist
->
file_index
==
map
->
file_idx
&&
ist
->
st
->
index
==
map
->
stream_idx
))
{
if
(
av_reallocp_array
(
&
ost
->
audio_channels_map
,
ost
->
audio_channels_mapped
+
1
,
sizeof
(
*
ost
->
audio_channels_map
)
)
<
0
)
report_and_exit
(
AVERROR
(
ENOMEM
));
ost
->
audio_channels_map
[
ost
->
audio_channels_mapped
++
]
=
map
->
channel_idx
;
}
}
}
#endif
}
else
check_streamcopy_filters
(
o
,
oc
,
ost
,
AVMEDIA_TYPE_AUDIO
);
return
ost
;
}
static
OutputStream
*
new_data_stream
(
Muxer
*
mux
,
const
OptionsContext
*
o
,
InputStream
*
ist
)
{
OutputStream
*
ost
;
ost
=
new_output_stream
(
mux
,
o
,
AVMEDIA_TYPE_DATA
,
ist
);
if
(
ost
->
enc_ctx
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Data stream encoding not supported yet (only streamcopy)
\n
"
);
exit_program
(
1
);
}
return
ost
;
}
static
OutputStream
*
new_unknown_stream
(
Muxer
*
mux
,
const
OptionsContext
*
o
,
InputStream
*
ist
)
{
OutputStream
*
ost
;
ost
=
new_output_stream
(
mux
,
o
,
AVMEDIA_TYPE_UNKNOWN
,
ist
);
if
(
ost
->
enc_ctx
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Unknown stream encoding not supported yet (only streamcopy)
\n
"
);
exit_program
(
1
);
}
return
ost
;
}
static
OutputStream
*
new_attachment_stream
(
Muxer
*
mux
,
const
OptionsContext
*
o
,
InputStream
*
ist
)
{
OutputStream
*
ost
=
new_output_stream
(
mux
,
o
,
AVMEDIA_TYPE_ATTACHMENT
,
ist
);
ost
->
finished
=
1
;
return
ost
;
}
static
OutputStream
*
new_subtitle_stream
(
Muxer
*
mux
,
const
OptionsContext
*
o
,
InputStream
*
ist
)
{
AVStream
*
st
;
OutputStream
*
ost
;
ost
=
new_output_stream
(
mux
,
o
,
AVMEDIA_TYPE_SUBTITLE
,
ist
);
st
=
ost
->
st
;
if
(
ost
->
enc_ctx
)
{
AVCodecContext
*
subtitle_enc
=
ost
->
enc_ctx
;
char
*
frame_size
=
NULL
;
MATCH_PER_STREAM_OPT
(
frame_sizes
,
str
,
frame_size
,
mux
->
fc
,
st
);
if
(
frame_size
&&
av_parse_video_size
(
&
subtitle_enc
->
width
,
&
subtitle_enc
->
height
,
frame_size
)
<
0
)
{
av_log
(
ost
,
AV_LOG_FATAL
,
"Invalid frame size: %s.
\n
"
,
frame_size
);
exit_program
(
1
);
}
}
return
ost
;
}
static
void
init_output_filter
(
OutputFilter
*
ofilter
,
const
OptionsContext
*
o
,
Muxer
*
mux
)
{
OutputStream
*
ost
;
switch
(
ofilter
->
type
)
{
case
AVMEDIA_TYPE_VIDEO
:
ost
=
new_video_stream
(
mux
,
o
,
NULL
);
break
;
case
AVMEDIA_TYPE_AUDIO
:
ost
=
new_audio_stream
(
mux
,
o
,
NULL
);
break
;
default:
av_log
(
mux
,
AV_LOG_FATAL
,
"Only video and audio filters are supported "
"currently.
\n
"
);
exit_program
(
1
);
}
ost
->
filter
=
ofilter
;
ofilter
->
ost
=
ost
;
ofilter
->
format
=
-
1
;
if
(
!
ost
->
enc_ctx
)
{
av_log
(
ost
,
AV_LOG_ERROR
,
"Streamcopy requested for output stream fed "
"from a complex filtergraph. Filtering and streamcopy "
"cannot be used together.
\n
"
);
exit_program
(
1
);
}
if
(
ost
->
avfilter
&&
(
ost
->
filters
||
ost
->
filters_script
))
{
const
char
*
opt
=
ost
->
filters
?
"-vf/-af/-filter"
:
"-filter_script"
;
av_log
(
ost
,
AV_LOG_ERROR
,
"%s '%s' was specified through the %s option "
"for output stream %d:%d, which is fed from a complex filtergraph.
\n
"
"%s and -filter_complex cannot be used together for the same stream.
\n
"
,
ost
->
filters
?
"Filtergraph"
:
"Filtergraph script"
,
ost
->
filters
?
ost
->
filters
:
ost
->
filters_script
,
opt
,
ost
->
file_index
,
ost
->
index
,
opt
);
exit_program
(
1
);
}
avfilter_inout_free
(
&
ofilter
->
out_tmp
);
}
static
void
map_auto_video
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
InputStream
*
best_ist
=
NULL
;
int
best_score
=
0
;
int
qcr
;
/* video: highest resolution */
if
(
av_guess_codec
(
oc
->
oformat
,
NULL
,
oc
->
url
,
NULL
,
AVMEDIA_TYPE_VIDEO
)
==
AV_CODEC_ID_NONE
)
return
;
qcr
=
avformat_query_codec
(
oc
->
oformat
,
oc
->
oformat
->
video_codec
,
0
);
for
(
int
j
=
0
;
j
<
nb_input_files
;
j
++
)
{
InputFile
*
ifile
=
input_files
[
j
];
InputStream
*
file_best_ist
=
NULL
;
int
file_best_score
=
0
;
for
(
int
i
=
0
;
i
<
ifile
->
nb_streams
;
i
++
)
{
InputStream
*
ist
=
ifile
->
streams
[
i
];
int
score
;
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
||
ist
->
st
->
codecpar
->
codec_type
!=
AVMEDIA_TYPE_VIDEO
)
continue
;
score
=
ist
->
st
->
codecpar
->
width
*
ist
->
st
->
codecpar
->
height
+
100000000
*
!!
(
ist
->
st
->
event_flags
&
AVSTREAM_EVENT_FLAG_NEW_PACKETS
)
+
5000000
*!!
(
ist
->
st
->
disposition
&
AV_DISPOSITION_DEFAULT
);
if
((
qcr
!=
MKTAG
(
'A'
,
'P'
,
'I'
,
'C'
))
&&
(
ist
->
st
->
disposition
&
AV_DISPOSITION_ATTACHED_PIC
))
score
=
1
;
if
(
score
>
file_best_score
)
{
if
((
qcr
==
MKTAG
(
'A'
,
'P'
,
'I'
,
'C'
))
&&
!
(
ist
->
st
->
disposition
&
AV_DISPOSITION_ATTACHED_PIC
))
continue
;
file_best_score
=
score
;
file_best_ist
=
ist
;
}
}
if
(
file_best_ist
)
{
if
((
qcr
==
MKTAG
(
'A'
,
'P'
,
'I'
,
'C'
))
||
!
(
file_best_ist
->
st
->
disposition
&
AV_DISPOSITION_ATTACHED_PIC
))
file_best_score
-=
5000000
*!!
(
file_best_ist
->
st
->
disposition
&
AV_DISPOSITION_DEFAULT
);
if
(
file_best_score
>
best_score
)
{
best_score
=
file_best_score
;
best_ist
=
file_best_ist
;
}
}
}
if
(
best_ist
)
new_video_stream
(
mux
,
o
,
best_ist
);
}
static
void
map_auto_audio
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
InputStream
*
best_ist
=
NULL
;
int
best_score
=
0
;
/* audio: most channels */
if
(
av_guess_codec
(
oc
->
oformat
,
NULL
,
oc
->
url
,
NULL
,
AVMEDIA_TYPE_AUDIO
)
==
AV_CODEC_ID_NONE
)
return
;
for
(
int
j
=
0
;
j
<
nb_input_files
;
j
++
)
{
InputFile
*
ifile
=
input_files
[
j
];
InputStream
*
file_best_ist
=
NULL
;
int
file_best_score
=
0
;
for
(
int
i
=
0
;
i
<
ifile
->
nb_streams
;
i
++
)
{
InputStream
*
ist
=
ifile
->
streams
[
i
];
int
score
;
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
||
ist
->
st
->
codecpar
->
codec_type
!=
AVMEDIA_TYPE_AUDIO
)
continue
;
score
=
ist
->
st
->
codecpar
->
ch_layout
.
nb_channels
+
100000000
*
!!
(
ist
->
st
->
event_flags
&
AVSTREAM_EVENT_FLAG_NEW_PACKETS
)
+
5000000
*!!
(
ist
->
st
->
disposition
&
AV_DISPOSITION_DEFAULT
);
if
(
score
>
file_best_score
)
{
file_best_score
=
score
;
file_best_ist
=
ist
;
}
}
if
(
file_best_ist
)
{
file_best_score
-=
5000000
*!!
(
file_best_ist
->
st
->
disposition
&
AV_DISPOSITION_DEFAULT
);
if
(
file_best_score
>
best_score
)
{
best_score
=
file_best_score
;
best_ist
=
file_best_ist
;
}
}
}
if
(
best_ist
)
new_audio_stream
(
mux
,
o
,
best_ist
);
}
static
void
map_auto_subtitle
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
char
*
subtitle_codec_name
=
NULL
;
/* subtitles: pick first */
MATCH_PER_TYPE_OPT
(
codec_names
,
str
,
subtitle_codec_name
,
oc
,
"s"
);
if
(
!
avcodec_find_encoder
(
oc
->
oformat
->
subtitle_codec
)
&&
!
subtitle_codec_name
)
return
;
for
(
InputStream
*
ist
=
ist_iter
(
NULL
);
ist
;
ist
=
ist_iter
(
ist
))
if
(
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_SUBTITLE
)
{
AVCodecDescriptor
const
*
input_descriptor
=
avcodec_descriptor_get
(
ist
->
st
->
codecpar
->
codec_id
);
AVCodecDescriptor
const
*
output_descriptor
=
NULL
;
AVCodec
const
*
output_codec
=
avcodec_find_encoder
(
oc
->
oformat
->
subtitle_codec
);
int
input_props
=
0
,
output_props
=
0
;
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
)
continue
;
if
(
output_codec
)
output_descriptor
=
avcodec_descriptor_get
(
output_codec
->
id
);
if
(
input_descriptor
)
input_props
=
input_descriptor
->
props
&
(
AV_CODEC_PROP_TEXT_SUB
|
AV_CODEC_PROP_BITMAP_SUB
);
if
(
output_descriptor
)
output_props
=
output_descriptor
->
props
&
(
AV_CODEC_PROP_TEXT_SUB
|
AV_CODEC_PROP_BITMAP_SUB
);
if
(
subtitle_codec_name
||
input_props
&
output_props
||
// Map dvb teletext which has neither property to any output subtitle encoder
(
input_descriptor
&&
output_descriptor
&&
(
!
input_descriptor
->
props
||
!
output_descriptor
->
props
)))
{
new_subtitle_stream
(
mux
,
o
,
ist
);
break
;
}
}
}
static
void
map_auto_data
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
/* Data only if codec id match */
enum
AVCodecID
codec_id
=
av_guess_codec
(
oc
->
oformat
,
NULL
,
oc
->
url
,
NULL
,
AVMEDIA_TYPE_DATA
);
if
(
codec_id
==
AV_CODEC_ID_NONE
)
return
;
for
(
InputStream
*
ist
=
ist_iter
(
NULL
);
ist
;
ist
=
ist_iter
(
ist
))
{
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
)
continue
;
if
(
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_DATA
&&
ist
->
st
->
codecpar
->
codec_id
==
codec_id
)
new_data_stream
(
mux
,
o
,
ist
);
}
}
static
void
map_manual
(
Muxer
*
mux
,
const
OptionsContext
*
o
,
const
StreamMap
*
map
)
{
InputStream
*
ist
;
if
(
map
->
disabled
)
return
;
if
(
map
->
linklabel
)
{
FilterGraph
*
fg
;
OutputFilter
*
ofilter
=
NULL
;
int
j
,
k
;
for
(
j
=
0
;
j
<
nb_filtergraphs
;
j
++
)
{
fg
=
filtergraphs
[
j
];
for
(
k
=
0
;
k
<
fg
->
nb_outputs
;
k
++
)
{
AVFilterInOut
*
out
=
fg
->
outputs
[
k
]
->
out_tmp
;
if
(
out
&&
!
strcmp
(
out
->
name
,
map
->
linklabel
))
{
ofilter
=
fg
->
outputs
[
k
];
goto
loop_end
;
}
}
}
loop_end:
if
(
!
ofilter
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Output with label '%s' does not exist "
"in any defined filter graph, or was already used elsewhere.
\n
"
,
map
->
linklabel
);
exit_program
(
1
);
}
init_output_filter
(
ofilter
,
o
,
mux
);
}
else
{
ist
=
input_files
[
map
->
file_index
]
->
streams
[
map
->
stream_index
];
if
(
ist
->
user_set_discard
==
AVDISCARD_ALL
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Stream #%d:%d is disabled and cannot be mapped.
\n
"
,
map
->
file_index
,
map
->
stream_index
);
exit_program
(
1
);
}
if
(
o
->
subtitle_disable
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_SUBTITLE
)
return
;
if
(
o
->
audio_disable
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_AUDIO
)
return
;
if
(
o
->
video_disable
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
)
return
;
if
(
o
->
data_disable
&&
ist
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_DATA
)
return
;
switch
(
ist
->
st
->
codecpar
->
codec_type
)
{
case
AVMEDIA_TYPE_VIDEO
:
new_video_stream
(
mux
,
o
,
ist
);
break
;
case
AVMEDIA_TYPE_AUDIO
:
new_audio_stream
(
mux
,
o
,
ist
);
break
;
case
AVMEDIA_TYPE_SUBTITLE
:
new_subtitle_stream
(
mux
,
o
,
ist
);
break
;
case
AVMEDIA_TYPE_DATA
:
new_data_stream
(
mux
,
o
,
ist
);
break
;
case
AVMEDIA_TYPE_ATTACHMENT
:
new_attachment_stream
(
mux
,
o
,
ist
);
break
;
case
AVMEDIA_TYPE_UNKNOWN
:
if
(
copy_unknown_streams
)
{
new_unknown_stream
(
mux
,
o
,
ist
);
break
;
}
default:
av_log
(
mux
,
ignore_unknown_streams
?
AV_LOG_WARNING
:
AV_LOG_FATAL
,
"Cannot map stream #%d:%d - unsupported type.
\n
"
,
map
->
file_index
,
map
->
stream_index
);
if
(
!
ignore_unknown_streams
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"If you want unsupported types ignored instead "
"of failing, please use the -ignore_unknown option
\n
"
"If you want them copied, please use -copy_unknown
\n
"
);
exit_program
(
1
);
}
}
}
}
static
void
of_add_attachments
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
OutputStream
*
ost
;
int
err
;
for
(
int
i
=
0
;
i
<
o
->
nb_attachments
;
i
++
)
{
AVIOContext
*
pb
;
uint8_t
*
attachment
;
const
char
*
p
;
int64_t
len
;
if
((
err
=
avio_open2
(
&
pb
,
o
->
attachments
[
i
],
AVIO_FLAG_READ
,
&
int_cb
,
NULL
))
<
0
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Could not open attachment file %s.
\n
"
,
o
->
attachments
[
i
]);
exit_program
(
1
);
}
if
((
len
=
avio_size
(
pb
))
<=
0
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Could not get size of the attachment %s.
\n
"
,
o
->
attachments
[
i
]);
exit_program
(
1
);
}
if
(
len
>
INT_MAX
-
AV_INPUT_BUFFER_PADDING_SIZE
||
!
(
attachment
=
av_malloc
(
len
+
AV_INPUT_BUFFER_PADDING_SIZE
)))
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Attachment %s too large.
\n
"
,
o
->
attachments
[
i
]);
exit_program
(
1
);
}
avio_read
(
pb
,
attachment
,
len
);
memset
(
attachment
+
len
,
0
,
AV_INPUT_BUFFER_PADDING_SIZE
);
ost
=
new_attachment_stream
(
mux
,
o
,
NULL
);
ost
->
attachment_filename
=
o
->
attachments
[
i
];
ost
->
st
->
codecpar
->
extradata
=
attachment
;
ost
->
st
->
codecpar
->
extradata_size
=
len
;
p
=
strrchr
(
o
->
attachments
[
i
],
'/'
);
av_dict_set
(
&
ost
->
st
->
metadata
,
"filename"
,
(
p
&&
*
p
)
?
p
+
1
:
o
->
attachments
[
i
],
AV_DICT_DONT_OVERWRITE
);
avio_closep
(
&
pb
);
}
}
static
void
create_streams
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
int
auto_disable_v
=
o
->
video_disable
;
int
auto_disable_a
=
o
->
audio_disable
;
int
auto_disable_s
=
o
->
subtitle_disable
;
int
auto_disable_d
=
o
->
data_disable
;
/* create streams for all unlabeled output pads */
for
(
int
i
=
0
;
i
<
nb_filtergraphs
;
i
++
)
{
FilterGraph
*
fg
=
filtergraphs
[
i
];
for
(
int
j
=
0
;
j
<
fg
->
nb_outputs
;
j
++
)
{
OutputFilter
*
ofilter
=
fg
->
outputs
[
j
];
if
(
!
ofilter
->
out_tmp
||
ofilter
->
out_tmp
->
name
)
continue
;
switch
(
ofilter
->
type
)
{
case
AVMEDIA_TYPE_VIDEO
:
auto_disable_v
=
1
;
break
;
case
AVMEDIA_TYPE_AUDIO
:
auto_disable_a
=
1
;
break
;
case
AVMEDIA_TYPE_SUBTITLE
:
auto_disable_s
=
1
;
break
;
}
init_output_filter
(
ofilter
,
o
,
mux
);
}
}
if
(
!
o
->
nb_stream_maps
)
{
/* pick the "best" stream of each type */
if
(
!
auto_disable_v
)
map_auto_video
(
mux
,
o
);
if
(
!
auto_disable_a
)
map_auto_audio
(
mux
,
o
);
if
(
!
auto_disable_s
)
map_auto_subtitle
(
mux
,
o
);
if
(
!
auto_disable_d
)
map_auto_data
(
mux
,
o
);
}
else
{
for
(
int
i
=
0
;
i
<
o
->
nb_stream_maps
;
i
++
)
map_manual
(
mux
,
o
,
&
o
->
stream_maps
[
i
]);
}
of_add_attachments
(
mux
,
o
);
if
(
!
oc
->
nb_streams
&&
!
(
oc
->
oformat
->
flags
&
AVFMT_NOSTREAMS
))
{
av_dump_format
(
oc
,
nb_output_files
-
1
,
oc
->
url
,
1
);
av_log
(
mux
,
AV_LOG_ERROR
,
"Output file does not contain any stream
\n
"
);
exit_program
(
1
);
}
}
static
int
setup_sync_queues
(
Muxer
*
mux
,
AVFormatContext
*
oc
,
int64_t
buf_size_us
)
{
OutputFile
*
of
=
&
mux
->
of
;
int
nb_av_enc
=
0
,
nb_interleaved
=
0
;
int
limit_frames
=
0
,
limit_frames_av_enc
=
0
;
#define IS_AV_ENC(ost, type) \
(ost->enc_ctx && (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO))
#define IS_INTERLEAVED(type) (type != AVMEDIA_TYPE_ATTACHMENT)
for
(
int
i
=
0
;
i
<
oc
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
of
->
streams
[
i
];
MuxStream
*
ms
=
ms_from_ost
(
ost
);
enum
AVMediaType
type
=
ost
->
st
->
codecpar
->
codec_type
;
ost
->
sq_idx_encode
=
-
1
;
ost
->
sq_idx_mux
=
-
1
;
nb_interleaved
+=
IS_INTERLEAVED
(
type
);
nb_av_enc
+=
IS_AV_ENC
(
ost
,
type
);
limit_frames
|=
ms
->
max_frames
<
INT64_MAX
;
limit_frames_av_enc
|=
(
ms
->
max_frames
<
INT64_MAX
)
&&
IS_AV_ENC
(
ost
,
type
);
}
if
(
!
((
nb_interleaved
>
1
&&
of
->
shortest
)
||
(
nb_interleaved
>
0
&&
limit_frames
)))
return
0
;
/* if we have more than one encoded audio/video streams, or at least
* one encoded audio/video stream is frame-limited, then we
* synchronize them before encoding */
if
((
of
->
shortest
&&
nb_av_enc
>
1
)
||
limit_frames_av_enc
)
{
of
->
sq_encode
=
sq_alloc
(
SYNC_QUEUE_FRAMES
,
buf_size_us
);
if
(
!
of
->
sq_encode
)
return
AVERROR
(
ENOMEM
);
for
(
int
i
=
0
;
i
<
oc
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
of
->
streams
[
i
];
MuxStream
*
ms
=
ms_from_ost
(
ost
);
enum
AVMediaType
type
=
ost
->
st
->
codecpar
->
codec_type
;
if
(
!
IS_AV_ENC
(
ost
,
type
))
continue
;
ost
->
sq_idx_encode
=
sq_add_stream
(
of
->
sq_encode
,
of
->
shortest
||
ms
->
max_frames
<
INT64_MAX
);
if
(
ost
->
sq_idx_encode
<
0
)
return
ost
->
sq_idx_encode
;
ost
->
sq_frame
=
av_frame_alloc
();
if
(
!
ost
->
sq_frame
)
return
AVERROR
(
ENOMEM
);
if
(
ms
->
max_frames
!=
INT64_MAX
)
sq_limit_frames
(
of
->
sq_encode
,
ost
->
sq_idx_encode
,
ms
->
max_frames
);
}
}
/* if there are any additional interleaved streams, then ALL the streams
* are also synchronized before sending them to the muxer */
if
(
nb_interleaved
>
nb_av_enc
)
{
mux
->
sq_mux
=
sq_alloc
(
SYNC_QUEUE_PACKETS
,
buf_size_us
);
if
(
!
mux
->
sq_mux
)
return
AVERROR
(
ENOMEM
);
mux
->
sq_pkt
=
av_packet_alloc
();
if
(
!
mux
->
sq_pkt
)
return
AVERROR
(
ENOMEM
);
for
(
int
i
=
0
;
i
<
oc
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
of
->
streams
[
i
];
MuxStream
*
ms
=
ms_from_ost
(
ost
);
enum
AVMediaType
type
=
ost
->
st
->
codecpar
->
codec_type
;
if
(
!
IS_INTERLEAVED
(
type
))
continue
;
ost
->
sq_idx_mux
=
sq_add_stream
(
mux
->
sq_mux
,
of
->
shortest
||
ms
->
max_frames
<
INT64_MAX
);
if
(
ost
->
sq_idx_mux
<
0
)
return
ost
->
sq_idx_mux
;
if
(
ms
->
max_frames
!=
INT64_MAX
)
sq_limit_frames
(
mux
->
sq_mux
,
ost
->
sq_idx_mux
,
ms
->
max_frames
);
}
}
#undef IS_AV_ENC
#undef IS_INTERLEAVED
return
0
;
}
static
void
of_add_programs
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
/* process manually set programs */
for
(
int
i
=
0
;
i
<
o
->
nb_program
;
i
++
)
{
const
char
*
p
=
o
->
program
[
i
].
u
.
str
;
int
progid
=
i
+
1
;
AVProgram
*
program
;
while
(
*
p
)
{
const
char
*
p2
=
av_get_token
(
&
p
,
":"
);
const
char
*
to_dealloc
=
p2
;
char
*
key
;
if
(
!
p2
)
break
;
if
(
*
p
)
p
++
;
key
=
av_get_token
(
&
p2
,
"="
);
if
(
!
key
||
!*
p2
)
{
av_freep
(
&
to_dealloc
);
av_freep
(
&
key
);
break
;
}
p2
++
;
if
(
!
strcmp
(
key
,
"program_num"
))
progid
=
strtol
(
p2
,
NULL
,
0
);
av_freep
(
&
to_dealloc
);
av_freep
(
&
key
);
}
program
=
av_new_program
(
oc
,
progid
);
if
(
!
program
)
report_and_exit
(
AVERROR
(
ENOMEM
));
p
=
o
->
program
[
i
].
u
.
str
;
while
(
*
p
)
{
const
char
*
p2
=
av_get_token
(
&
p
,
":"
);
const
char
*
to_dealloc
=
p2
;
char
*
key
;
if
(
!
p2
)
break
;
if
(
*
p
)
p
++
;
key
=
av_get_token
(
&
p2
,
"="
);
if
(
!
key
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"No '=' character in program string %s.
\n
"
,
p2
);
exit_program
(
1
);
}
if
(
!*
p2
)
exit_program
(
1
);
p2
++
;
if
(
!
strcmp
(
key
,
"title"
))
{
av_dict_set
(
&
program
->
metadata
,
"title"
,
p2
,
0
);
}
else
if
(
!
strcmp
(
key
,
"program_num"
))
{
}
else
if
(
!
strcmp
(
key
,
"st"
))
{
int
st_num
=
strtol
(
p2
,
NULL
,
0
);
av_program_add_stream_index
(
oc
,
progid
,
st_num
);
}
else
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Unknown program key %s.
\n
"
,
key
);
exit_program
(
1
);
}
av_freep
(
&
to_dealloc
);
av_freep
(
&
key
);
}
}
}
/**
* Parse a metadata specifier passed as 'arg' parameter.
* @param arg metadata string to parse
* @param type metadata type is written here -- g(lobal)/s(tream)/c(hapter)/p(rogram)
* @param index for type c/p, chapter/program index is written here
* @param stream_spec for type s, the stream specifier is written here
*/
static
void
parse_meta_type
(
void
*
logctx
,
const
char
*
arg
,
char
*
type
,
int
*
index
,
const
char
**
stream_spec
)
{
if
(
*
arg
)
{
*
type
=
*
arg
;
switch
(
*
arg
)
{
case
'g'
:
break
;
case
's'
:
if
(
*
(
++
arg
)
&&
*
arg
!=
':'
)
{
av_log
(
logctx
,
AV_LOG_FATAL
,
"Invalid metadata specifier %s.
\n
"
,
arg
);
exit_program
(
1
);
}
*
stream_spec
=
*
arg
==
':'
?
arg
+
1
:
""
;
break
;
case
'c'
:
case
'p'
:
if
(
*
(
++
arg
)
==
':'
)
*
index
=
strtol
(
++
arg
,
NULL
,
0
);
break
;
default:
av_log
(
logctx
,
AV_LOG_FATAL
,
"Invalid metadata type %c.
\n
"
,
*
arg
);
exit_program
(
1
);
}
}
else
*
type
=
'g'
;
}
static
void
of_add_metadata
(
OutputFile
*
of
,
AVFormatContext
*
oc
,
const
OptionsContext
*
o
)
{
for
(
int
i
=
0
;
i
<
o
->
nb_metadata
;
i
++
)
{
AVDictionary
**
m
;
char
type
,
*
val
;
const
char
*
stream_spec
;
int
index
=
0
,
ret
=
0
;
val
=
strchr
(
o
->
metadata
[
i
].
u
.
str
,
'='
);
if
(
!
val
)
{
av_log
(
of
,
AV_LOG_FATAL
,
"No '=' character in metadata string %s.
\n
"
,
o
->
metadata
[
i
].
u
.
str
);
exit_program
(
1
);
}
*
val
++
=
0
;
parse_meta_type
(
of
,
o
->
metadata
[
i
].
specifier
,
&
type
,
&
index
,
&
stream_spec
);
if
(
type
==
's'
)
{
for
(
int
j
=
0
;
j
<
oc
->
nb_streams
;
j
++
)
{
OutputStream
*
ost
=
of
->
streams
[
j
];
if
((
ret
=
check_stream_specifier
(
oc
,
oc
->
streams
[
j
],
stream_spec
))
>
0
)
{
#if FFMPEG_ROTATION_METADATA
if
(
!
strcmp
(
o
->
metadata
[
i
].
u
.
str
,
"rotate"
))
{
char
*
tail
;
double
theta
=
av_strtod
(
val
,
&
tail
);
if
(
!*
tail
)
{
ost
->
rotate_overridden
=
1
;
ost
->
rotate_override_value
=
theta
;
}
av_log
(
ost
,
AV_LOG_WARNING
,
"Conversion of a 'rotate' metadata key to a "
"proper display matrix rotation is deprecated. "
"See -display_rotation for setting rotation "
"instead."
);
}
else
{
#endif
av_dict_set
(
&
oc
->
streams
[
j
]
->
metadata
,
o
->
metadata
[
i
].
u
.
str
,
*
val
?
val
:
NULL
,
0
);
#if FFMPEG_ROTATION_METADATA
}
#endif
}
else
if
(
ret
<
0
)
exit_program
(
1
);
}
}
else
{
switch
(
type
)
{
case
'g'
:
m
=
&
oc
->
metadata
;
break
;
case
'c'
:
if
(
index
<
0
||
index
>=
oc
->
nb_chapters
)
{
av_log
(
of
,
AV_LOG_FATAL
,
"Invalid chapter index %d in metadata specifier.
\n
"
,
index
);
exit_program
(
1
);
}
m
=
&
oc
->
chapters
[
index
]
->
metadata
;
break
;
case
'p'
:
if
(
index
<
0
||
index
>=
oc
->
nb_programs
)
{
av_log
(
of
,
AV_LOG_FATAL
,
"Invalid program index %d in metadata specifier.
\n
"
,
index
);
exit_program
(
1
);
}
m
=
&
oc
->
programs
[
index
]
->
metadata
;
break
;
default:
av_log
(
of
,
AV_LOG_FATAL
,
"Invalid metadata specifier %s.
\n
"
,
o
->
metadata
[
i
].
specifier
);
exit_program
(
1
);
}
av_dict_set
(
m
,
o
->
metadata
[
i
].
u
.
str
,
*
val
?
val
:
NULL
,
0
);
}
}
}
static
void
set_channel_layout
(
OutputFilter
*
f
,
OutputStream
*
ost
)
{
const
AVCodec
*
c
=
ost
->
enc_ctx
->
codec
;
int
i
,
err
;
if
(
ost
->
enc_ctx
->
ch_layout
.
order
!=
AV_CHANNEL_ORDER_UNSPEC
)
{
/* Pass the layout through for all orders but UNSPEC */
err
=
av_channel_layout_copy
(
&
f
->
ch_layout
,
&
ost
->
enc_ctx
->
ch_layout
);
if
(
err
<
0
)
report_and_exit
(
AVERROR
(
ENOMEM
));
return
;
}
/* Requested layout is of order UNSPEC */
if
(
!
c
->
ch_layouts
)
{
/* Use the default native layout for the requested amount of channels when the
encoder doesn't have a list of supported layouts */
av_channel_layout_default
(
&
f
->
ch_layout
,
ost
->
enc_ctx
->
ch_layout
.
nb_channels
);
return
;
}
/* Encoder has a list of supported layouts. Pick the first layout in it with the
same amount of channels as the requested layout */
for
(
i
=
0
;
c
->
ch_layouts
[
i
].
nb_channels
;
i
++
)
{
if
(
c
->
ch_layouts
[
i
].
nb_channels
==
ost
->
enc_ctx
->
ch_layout
.
nb_channels
)
break
;
}
if
(
c
->
ch_layouts
[
i
].
nb_channels
)
{
/* Use it if one is found */
err
=
av_channel_layout_copy
(
&
f
->
ch_layout
,
&
c
->
ch_layouts
[
i
]);
if
(
err
<
0
)
report_and_exit
(
AVERROR
(
ENOMEM
));
return
;
}
/* If no layout for the amount of channels requested was found, use the default
native layout for it. */
av_channel_layout_default
(
&
f
->
ch_layout
,
ost
->
enc_ctx
->
ch_layout
.
nb_channels
);
}
static
int
copy_chapters
(
InputFile
*
ifile
,
OutputFile
*
ofile
,
AVFormatContext
*
os
,
int
copy_metadata
)
{
AVFormatContext
*
is
=
ifile
->
ctx
;
AVChapter
**
tmp
;
int
i
;
tmp
=
av_realloc_f
(
os
->
chapters
,
is
->
nb_chapters
+
os
->
nb_chapters
,
sizeof
(
*
os
->
chapters
));
if
(
!
tmp
)
return
AVERROR
(
ENOMEM
);
os
->
chapters
=
tmp
;
for
(
i
=
0
;
i
<
is
->
nb_chapters
;
i
++
)
{
AVChapter
*
in_ch
=
is
->
chapters
[
i
],
*
out_ch
;
int64_t
start_time
=
(
ofile
->
start_time
==
AV_NOPTS_VALUE
)
?
0
:
ofile
->
start_time
;
int64_t
ts_off
=
av_rescale_q
(
start_time
-
ifile
->
ts_offset
,
AV_TIME_BASE_Q
,
in_ch
->
time_base
);
int64_t
rt
=
(
ofile
->
recording_time
==
INT64_MAX
)
?
INT64_MAX
:
av_rescale_q
(
ofile
->
recording_time
,
AV_TIME_BASE_Q
,
in_ch
->
time_base
);
if
(
in_ch
->
end
<
ts_off
)
continue
;
if
(
rt
!=
INT64_MAX
&&
in_ch
->
start
>
rt
+
ts_off
)
break
;
out_ch
=
av_mallocz
(
sizeof
(
AVChapter
));
if
(
!
out_ch
)
return
AVERROR
(
ENOMEM
);
out_ch
->
id
=
in_ch
->
id
;
out_ch
->
time_base
=
in_ch
->
time_base
;
out_ch
->
start
=
FFMAX
(
0
,
in_ch
->
start
-
ts_off
);
out_ch
->
end
=
FFMIN
(
rt
,
in_ch
->
end
-
ts_off
);
if
(
copy_metadata
)
av_dict_copy
(
&
out_ch
->
metadata
,
in_ch
->
metadata
,
0
);
os
->
chapters
[
os
->
nb_chapters
++
]
=
out_ch
;
}
return
0
;
}
static
int
copy_metadata
(
Muxer
*
mux
,
AVFormatContext
*
ic
,
const
char
*
outspec
,
const
char
*
inspec
,
int
*
metadata_global_manual
,
int
*
metadata_streams_manual
,
int
*
metadata_chapters_manual
,
const
OptionsContext
*
o
)
{
AVFormatContext
*
oc
=
mux
->
fc
;
AVDictionary
**
meta_in
=
NULL
;
AVDictionary
**
meta_out
=
NULL
;
int
i
,
ret
=
0
;
char
type_in
,
type_out
;
const
char
*
istream_spec
=
NULL
,
*
ostream_spec
=
NULL
;
int
idx_in
=
0
,
idx_out
=
0
;
parse_meta_type
(
mux
,
inspec
,
&
type_in
,
&
idx_in
,
&
istream_spec
);
parse_meta_type
(
mux
,
outspec
,
&
type_out
,
&
idx_out
,
&
ostream_spec
);
if
(
type_in
==
'g'
||
type_out
==
'g'
)
*
metadata_global_manual
=
1
;
if
(
type_in
==
's'
||
type_out
==
's'
)
*
metadata_streams_manual
=
1
;
if
(
type_in
==
'c'
||
type_out
==
'c'
)
*
metadata_chapters_manual
=
1
;
/* ic is NULL when just disabling automatic mappings */
if
(
!
ic
)
return
0
;
#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
if ((index) < 0 || (index) >= (nb_elems)) {\
av_log(mux, AV_LOG_FATAL, "Invalid %s index %d while processing metadata maps.\n",\
(desc), (index));\
exit_program(1);\
}
#define SET_DICT(type, meta, context, index)\
switch (type) {\
case 'g':\
meta = &context->metadata;\
break;\
case 'c':\
METADATA_CHECK_INDEX(index, context->nb_chapters, "chapter")\
meta = &context->chapters[index]->metadata;\
break;\
case 'p':\
METADATA_CHECK_INDEX(index, context->nb_programs, "program")\
meta = &context->programs[index]->metadata;\
break;\
case 's':\
break;
/* handled separately below */
\
default: av_assert0(0);\
}\
SET_DICT
(
type_in
,
meta_in
,
ic
,
idx_in
);
SET_DICT
(
type_out
,
meta_out
,
oc
,
idx_out
);
/* for input streams choose first matching stream */
if
(
type_in
==
's'
)
{
for
(
i
=
0
;
i
<
ic
->
nb_streams
;
i
++
)
{
if
((
ret
=
check_stream_specifier
(
ic
,
ic
->
streams
[
i
],
istream_spec
))
>
0
)
{
meta_in
=
&
ic
->
streams
[
i
]
->
metadata
;
break
;
}
else
if
(
ret
<
0
)
exit_program
(
1
);
}
if
(
!
meta_in
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Stream specifier %s does not match any streams.
\n
"
,
istream_spec
);
exit_program
(
1
);
}
}
if
(
type_out
==
's'
)
{
for
(
i
=
0
;
i
<
oc
->
nb_streams
;
i
++
)
{
if
((
ret
=
check_stream_specifier
(
oc
,
oc
->
streams
[
i
],
ostream_spec
))
>
0
)
{
meta_out
=
&
oc
->
streams
[
i
]
->
metadata
;
av_dict_copy
(
meta_out
,
*
meta_in
,
AV_DICT_DONT_OVERWRITE
);
}
else
if
(
ret
<
0
)
exit_program
(
1
);
}
}
else
av_dict_copy
(
meta_out
,
*
meta_in
,
AV_DICT_DONT_OVERWRITE
);
return
0
;
}
static
void
copy_meta
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
OutputFile
*
of
=
&
mux
->
of
;
AVFormatContext
*
oc
=
mux
->
fc
;
int
chapters_input_file
=
o
->
chapters_input_file
;
int
metadata_global_manual
=
0
;
int
metadata_streams_manual
=
0
;
int
metadata_chapters_manual
=
0
;
/* copy metadata */
for
(
int
i
=
0
;
i
<
o
->
nb_metadata_map
;
i
++
)
{
char
*
p
;
int
in_file_index
=
strtol
(
o
->
metadata_map
[
i
].
u
.
str
,
&
p
,
0
);
if
(
in_file_index
>=
nb_input_files
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Invalid input file index %d while "
"processing metadata maps
\n
"
,
in_file_index
);
exit_program
(
1
);
}
copy_metadata
(
mux
,
in_file_index
>=
0
?
input_files
[
in_file_index
]
->
ctx
:
NULL
,
o
->
metadata_map
[
i
].
specifier
,
*
p
?
p
+
1
:
p
,
&
metadata_global_manual
,
&
metadata_streams_manual
,
&
metadata_chapters_manual
,
o
);
}
/* copy chapters */
if
(
chapters_input_file
>=
nb_input_files
)
{
if
(
chapters_input_file
==
INT_MAX
)
{
/* copy chapters from the first input file that has them*/
chapters_input_file
=
-
1
;
for
(
int
i
=
0
;
i
<
nb_input_files
;
i
++
)
if
(
input_files
[
i
]
->
ctx
->
nb_chapters
)
{
chapters_input_file
=
i
;
break
;
}
}
else
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Invalid input file index %d in chapter mapping.
\n
"
,
chapters_input_file
);
exit_program
(
1
);
}
}
if
(
chapters_input_file
>=
0
)
copy_chapters
(
input_files
[
chapters_input_file
],
of
,
oc
,
!
metadata_chapters_manual
);
/* copy global metadata by default */
if
(
!
metadata_global_manual
&&
nb_input_files
){
av_dict_copy
(
&
oc
->
metadata
,
input_files
[
0
]
->
ctx
->
metadata
,
AV_DICT_DONT_OVERWRITE
);
if
(
of
->
recording_time
!=
INT64_MAX
)
av_dict_set
(
&
oc
->
metadata
,
"duration"
,
NULL
,
0
);
av_dict_set
(
&
oc
->
metadata
,
"creation_time"
,
NULL
,
0
);
av_dict_set
(
&
oc
->
metadata
,
"company_name"
,
NULL
,
0
);
av_dict_set
(
&
oc
->
metadata
,
"product_name"
,
NULL
,
0
);
av_dict_set
(
&
oc
->
metadata
,
"product_version"
,
NULL
,
0
);
}
if
(
!
metadata_streams_manual
)
for
(
int
i
=
0
;
i
<
of
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
of
->
streams
[
i
];
if
(
!
ost
->
ist
)
/* this is true e.g. for attached files */
continue
;
av_dict_copy
(
&
ost
->
st
->
metadata
,
ost
->
ist
->
st
->
metadata
,
AV_DICT_DONT_OVERWRITE
);
if
(
ost
->
enc_ctx
)
{
av_dict_set
(
&
ost
->
st
->
metadata
,
"encoder"
,
NULL
,
0
);
}
}
}
static
int
set_dispositions
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
OutputFile
*
of
=
&
mux
->
of
;
AVFormatContext
*
ctx
=
mux
->
fc
;
int
nb_streams
[
AVMEDIA_TYPE_NB
]
=
{
0
};
int
have_default
[
AVMEDIA_TYPE_NB
]
=
{
0
};
int
have_manual
=
0
;
int
ret
=
0
;
const
char
**
dispositions
;
dispositions
=
av_calloc
(
ctx
->
nb_streams
,
sizeof
(
*
dispositions
));
if
(
!
dispositions
)
return
AVERROR
(
ENOMEM
);
// first, copy the input dispositions
for
(
int
i
=
0
;
i
<
ctx
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
of
->
streams
[
i
];
nb_streams
[
ost
->
st
->
codecpar
->
codec_type
]
++
;
MATCH_PER_STREAM_OPT
(
disposition
,
str
,
dispositions
[
i
],
ctx
,
ost
->
st
);
have_manual
|=
!!
dispositions
[
i
];
if
(
ost
->
ist
)
{
ost
->
st
->
disposition
=
ost
->
ist
->
st
->
disposition
;
if
(
ost
->
st
->
disposition
&
AV_DISPOSITION_DEFAULT
)
have_default
[
ost
->
st
->
codecpar
->
codec_type
]
=
1
;
}
}
if
(
have_manual
)
{
// process manually set dispositions - they override the above copy
for
(
int
i
=
0
;
i
<
ctx
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
of
->
streams
[
i
];
const
char
*
disp
=
dispositions
[
i
];
if
(
!
disp
)
continue
;
ret
=
av_opt_set
(
ost
->
st
,
"disposition"
,
disp
,
0
);
if
(
ret
<
0
)
goto
finish
;
}
}
else
{
// For each media type with more than one stream, find a suitable stream to
// mark as default, unless one is already marked default.
// "Suitable" means the first of that type, skipping attached pictures.
for
(
int
i
=
0
;
i
<
ctx
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
of
->
streams
[
i
];
enum
AVMediaType
type
=
ost
->
st
->
codecpar
->
codec_type
;
if
(
nb_streams
[
type
]
<
2
||
have_default
[
type
]
||
ost
->
st
->
disposition
&
AV_DISPOSITION_ATTACHED_PIC
)
continue
;
ost
->
st
->
disposition
|=
AV_DISPOSITION_DEFAULT
;
have_default
[
type
]
=
1
;
}
}
finish:
av_freep
(
&
dispositions
);
return
ret
;
}
const
char
*
const
forced_keyframes_const_names
[]
=
{
"n"
,
"n_forced"
,
"prev_forced_n"
,
"prev_forced_t"
,
"t"
,
NULL
};
static
int
compare_int64
(
const
void
*
a
,
const
void
*
b
)
{
return
FFDIFFSIGN
(
*
(
const
int64_t
*
)
a
,
*
(
const
int64_t
*
)
b
);
}
static
void
parse_forced_key_frames
(
KeyframeForceCtx
*
kf
,
const
Muxer
*
mux
,
const
char
*
spec
)
{
const
char
*
p
;
int
n
=
1
,
i
,
size
,
index
=
0
;
int64_t
t
,
*
pts
;
for
(
p
=
spec
;
*
p
;
p
++
)
if
(
*
p
==
','
)
n
++
;
size
=
n
;
pts
=
av_malloc_array
(
size
,
sizeof
(
*
pts
));
if
(
!
pts
)
report_and_exit
(
AVERROR
(
ENOMEM
));
p
=
spec
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
char
*
next
=
strchr
(
p
,
','
);
if
(
next
)
*
next
++
=
0
;
if
(
!
memcmp
(
p
,
"chapters"
,
8
))
{
AVChapter
*
const
*
ch
=
mux
->
fc
->
chapters
;
unsigned
int
nb_ch
=
mux
->
fc
->
nb_chapters
;
int
j
;
if
(
nb_ch
>
INT_MAX
-
size
||
!
(
pts
=
av_realloc_f
(
pts
,
size
+=
nb_ch
-
1
,
sizeof
(
*
pts
))))
report_and_exit
(
AVERROR
(
ENOMEM
));
t
=
p
[
8
]
?
parse_time_or_die
(
"force_key_frames"
,
p
+
8
,
1
)
:
0
;
for
(
j
=
0
;
j
<
nb_ch
;
j
++
)
{
const
AVChapter
*
c
=
ch
[
j
];
av_assert1
(
index
<
size
);
pts
[
index
++
]
=
av_rescale_q
(
c
->
start
,
c
->
time_base
,
AV_TIME_BASE_Q
)
+
t
;
}
}
else
{
av_assert1
(
index
<
size
);
pts
[
index
++
]
=
parse_time_or_die
(
"force_key_frames"
,
p
,
1
);
}
p
=
next
;
}
av_assert0
(
index
==
size
);
qsort
(
pts
,
size
,
sizeof
(
*
pts
),
compare_int64
);
kf
->
nb_pts
=
size
;
kf
->
pts
=
pts
;
}
static
int
process_forced_keyframes
(
Muxer
*
mux
,
const
OptionsContext
*
o
)
{
for
(
int
i
=
0
;
i
<
mux
->
of
.
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
mux
->
of
.
streams
[
i
];
const
char
*
forced_keyframes
=
NULL
;
MATCH_PER_STREAM_OPT
(
forced_key_frames
,
str
,
forced_keyframes
,
mux
->
fc
,
ost
->
st
);
if
(
!
(
ost
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
&&
ost
->
enc_ctx
&&
forced_keyframes
))
continue
;
if
(
!
strncmp
(
forced_keyframes
,
"expr:"
,
5
))
{
int
ret
=
av_expr_parse
(
&
ost
->
kf
.
pexpr
,
forced_keyframes
+
5
,
forced_keyframes_const_names
,
NULL
,
NULL
,
NULL
,
NULL
,
0
,
NULL
);
if
(
ret
<
0
)
{
av_log
(
ost
,
AV_LOG_ERROR
,
"Invalid force_key_frames expression '%s'
\n
"
,
forced_keyframes
+
5
);
return
ret
;
}
ost
->
kf
.
expr_const_values
[
FKF_N
]
=
0
;
ost
->
kf
.
expr_const_values
[
FKF_N_FORCED
]
=
0
;
ost
->
kf
.
expr_const_values
[
FKF_PREV_FORCED_N
]
=
NAN
;
ost
->
kf
.
expr_const_values
[
FKF_PREV_FORCED_T
]
=
NAN
;
// Don't parse the 'forced_keyframes' in case of 'keep-source-keyframes',
// parse it only for static kf timings
}
else
if
(
!
strcmp
(
forced_keyframes
,
"source"
))
{
ost
->
kf
.
type
=
KF_FORCE_SOURCE
;
}
else
if
(
!
strcmp
(
forced_keyframes
,
"source_no_drop"
))
{
ost
->
kf
.
type
=
KF_FORCE_SOURCE_NO_DROP
;
}
else
{
parse_forced_key_frames
(
&
ost
->
kf
,
mux
,
forced_keyframes
);
}
}
return
0
;
}
static
void
validate_enc_avopt
(
Muxer
*
mux
,
const
AVDictionary
*
codec_avopt
)
{
const
AVClass
*
class
=
avcodec_get_class
();
const
AVClass
*
fclass
=
avformat_get_class
();
const
OutputFile
*
of
=
&
mux
->
of
;
AVDictionary
*
unused_opts
;
const
AVDictionaryEntry
*
e
;
unused_opts
=
strip_specifiers
(
codec_avopt
);
for
(
int
i
=
0
;
i
<
of
->
nb_streams
;
i
++
)
{
e
=
NULL
;
while
((
e
=
av_dict_iterate
(
of
->
streams
[
i
]
->
encoder_opts
,
e
)))
av_dict_set
(
&
unused_opts
,
e
->
key
,
NULL
,
0
);
}
e
=
NULL
;
while
((
e
=
av_dict_iterate
(
unused_opts
,
e
)))
{
const
AVOption
*
option
=
av_opt_find
(
&
class
,
e
->
key
,
NULL
,
0
,
AV_OPT_SEARCH_CHILDREN
|
AV_OPT_SEARCH_FAKE_OBJ
);
const
AVOption
*
foption
=
av_opt_find
(
&
fclass
,
e
->
key
,
NULL
,
0
,
AV_OPT_SEARCH_CHILDREN
|
AV_OPT_SEARCH_FAKE_OBJ
);
if
(
!
option
||
foption
)
continue
;
if
(
!
(
option
->
flags
&
AV_OPT_FLAG_ENCODING_PARAM
))
{
av_log
(
mux
,
AV_LOG_ERROR
,
"Codec AVOption %s (%s) is not an "
"encoding option.
\n
"
,
e
->
key
,
option
->
help
?
option
->
help
:
""
);
exit_program
(
1
);
}
// gop_timecode is injected by generic code but not always used
if
(
!
strcmp
(
e
->
key
,
"gop_timecode"
))
continue
;
av_log
(
mux
,
AV_LOG_WARNING
,
"Codec AVOption %s (%s) has not been used "
"for any stream. The most likely reason is either wrong type "
"(e.g. a video option with no video streams) or that it is a "
"private option of some encoder which was not actually used for "
"any stream.
\n
"
,
e
->
key
,
option
->
help
?
option
->
help
:
""
);
}
av_dict_free
(
&
unused_opts
);
}
static
const
char
*
output_file_item_name
(
void
*
obj
)
{
const
Muxer
*
mux
=
obj
;
return
mux
->
log_name
;
}
static
const
AVClass
output_file_class
=
{
.
class_name
=
"OutputFile"
,
.
version
=
LIBAVUTIL_VERSION_INT
,
.
item_name
=
output_file_item_name
,
.
category
=
AV_CLASS_CATEGORY_MUXER
,
};
static
Muxer
*
mux_alloc
(
void
)
{
Muxer
*
mux
=
allocate_array_elem
(
&
output_files
,
sizeof
(
*
mux
),
&
nb_output_files
);
mux
->
of
.
clazz
=
&
output_file_class
;
mux
->
of
.
index
=
nb_output_files
-
1
;
snprintf
(
mux
->
log_name
,
sizeof
(
mux
->
log_name
),
"out#%d"
,
mux
->
of
.
index
);
return
mux
;
}
int
of_open
(
const
OptionsContext
*
o
,
const
char
*
filename
)
{
Muxer
*
mux
;
AVFormatContext
*
oc
;
int
err
;
OutputFile
*
of
;
int64_t
recording_time
=
o
->
recording_time
;
int64_t
stop_time
=
o
->
stop_time
;
mux
=
mux_alloc
();
of
=
&
mux
->
of
;
if
(
stop_time
!=
INT64_MAX
&&
recording_time
!=
INT64_MAX
)
{
stop_time
=
INT64_MAX
;
av_log
(
mux
,
AV_LOG_WARNING
,
"-t and -to cannot be used together; using -t.
\n
"
);
}
if
(
stop_time
!=
INT64_MAX
&&
recording_time
==
INT64_MAX
)
{
int64_t
start_time
=
o
->
start_time
==
AV_NOPTS_VALUE
?
0
:
o
->
start_time
;
if
(
stop_time
<=
start_time
)
{
av_log
(
mux
,
AV_LOG_ERROR
,
"-to value smaller than -ss; aborting.
\n
"
);
exit_program
(
1
);
}
else
{
recording_time
=
stop_time
-
start_time
;
}
}
of
->
recording_time
=
recording_time
;
of
->
start_time
=
o
->
start_time
;
of
->
shortest
=
o
->
shortest
;
mux
->
thread_queue_size
=
o
->
thread_queue_size
>
0
?
o
->
thread_queue_size
:
8
;
mux
->
limit_filesize
=
o
->
limit_filesize
;
av_dict_copy
(
&
mux
->
opts
,
o
->
g
->
format_opts
,
0
);
if
(
!
strcmp
(
filename
,
"-"
))
filename
=
"pipe:"
;
err
=
avformat_alloc_output_context2
(
&
oc
,
NULL
,
o
->
format
,
filename
);
if
(
!
oc
)
{
print_error
(
filename
,
err
);
exit_program
(
1
);
}
mux
->
fc
=
oc
;
av_strlcat
(
mux
->
log_name
,
"/"
,
sizeof
(
mux
->
log_name
));
av_strlcat
(
mux
->
log_name
,
oc
->
oformat
->
name
,
sizeof
(
mux
->
log_name
));
if
(
strcmp
(
oc
->
oformat
->
name
,
"rtp"
))
want_sdp
=
0
;
of
->
format
=
oc
->
oformat
;
if
(
recording_time
!=
INT64_MAX
)
oc
->
duration
=
recording_time
;
oc
->
interrupt_callback
=
int_cb
;
if
(
o
->
bitexact
)
{
oc
->
flags
|=
AVFMT_FLAG_BITEXACT
;
of
->
bitexact
=
1
;
}
else
{
of
->
bitexact
=
check_opt_bitexact
(
oc
,
mux
->
opts
,
"fflags"
,
AVFMT_FLAG_BITEXACT
);
}
/* create all output streams for this file */
create_streams
(
mux
,
o
);
/* check if all codec options have been used */
validate_enc_avopt
(
mux
,
o
->
g
->
codec_opts
);
/* set the decoding_needed flags and create simple filtergraphs */
for
(
int
i
=
0
;
i
<
of
->
nb_streams
;
i
++
)
{
OutputStream
*
ost
=
of
->
streams
[
i
];
if
(
ost
->
enc_ctx
&&
ost
->
ist
)
{
InputStream
*
ist
=
ost
->
ist
;
ist
->
decoding_needed
|=
DECODING_FOR_OST
;
ist
->
processing_needed
=
1
;
if
(
ost
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_VIDEO
||
ost
->
st
->
codecpar
->
codec_type
==
AVMEDIA_TYPE_AUDIO
)
{
err
=
init_simple_filtergraph
(
ist
,
ost
);
if
(
err
<
0
)
{
av_log
(
ost
,
AV_LOG_ERROR
,
"Error initializing a simple filtergraph
\n
"
);
exit_program
(
1
);
}
}
}
else
if
(
ost
->
ist
)
{
ost
->
ist
->
processing_needed
=
1
;
}
/* set the filter output constraints */
if
(
ost
->
filter
)
{
const
AVCodec
*
c
=
ost
->
enc_ctx
->
codec
;
OutputFilter
*
f
=
ost
->
filter
;
switch
(
ost
->
enc_ctx
->
codec_type
)
{
case
AVMEDIA_TYPE_VIDEO
:
f
->
frame_rate
=
ost
->
frame_rate
;
f
->
width
=
ost
->
enc_ctx
->
width
;
f
->
height
=
ost
->
enc_ctx
->
height
;
if
(
ost
->
enc_ctx
->
pix_fmt
!=
AV_PIX_FMT_NONE
)
{
f
->
format
=
ost
->
enc_ctx
->
pix_fmt
;
}
else
{
f
->
formats
=
c
->
pix_fmts
;
}
break
;
case
AVMEDIA_TYPE_AUDIO
:
if
(
ost
->
enc_ctx
->
sample_fmt
!=
AV_SAMPLE_FMT_NONE
)
{
f
->
format
=
ost
->
enc_ctx
->
sample_fmt
;
}
else
{
f
->
formats
=
c
->
sample_fmts
;
}
if
(
ost
->
enc_ctx
->
sample_rate
)
{
f
->
sample_rate
=
ost
->
enc_ctx
->
sample_rate
;
}
else
{
f
->
sample_rates
=
c
->
supported_samplerates
;
}
if
(
ost
->
enc_ctx
->
ch_layout
.
nb_channels
)
{
set_channel_layout
(
f
,
ost
);
}
else
if
(
c
->
ch_layouts
)
{
f
->
ch_layouts
=
c
->
ch_layouts
;
}
break
;
}
}
}
/* check filename in case of an image number is expected */
if
(
oc
->
oformat
->
flags
&
AVFMT_NEEDNUMBER
)
{
if
(
!
av_filename_number_test
(
oc
->
url
))
{
print_error
(
oc
->
url
,
AVERROR
(
EINVAL
));
exit_program
(
1
);
}
}
if
(
!
(
oc
->
oformat
->
flags
&
AVFMT_NOFILE
))
{
/* test if it already exists to avoid losing precious files */
assert_file_overwrite
(
filename
);
/* open the file */
if
((
err
=
avio_open2
(
&
oc
->
pb
,
filename
,
AVIO_FLAG_WRITE
,
&
oc
->
interrupt_callback
,
&
mux
->
opts
))
<
0
)
{
print_error
(
filename
,
err
);
exit_program
(
1
);
}
}
else
if
(
strcmp
(
oc
->
oformat
->
name
,
"image2"
)
==
0
&&
!
av_filename_number_test
(
filename
))
assert_file_overwrite
(
filename
);
if
(
o
->
mux_preload
)
{
av_dict_set_int
(
&
mux
->
opts
,
"preload"
,
o
->
mux_preload
*
AV_TIME_BASE
,
0
);
}
oc
->
max_delay
=
(
int
)(
o
->
mux_max_delay
*
AV_TIME_BASE
);
/* copy metadata and chapters from input files */
copy_meta
(
mux
,
o
);
of_add_programs
(
mux
,
o
);
of_add_metadata
(
of
,
oc
,
o
);
err
=
set_dispositions
(
mux
,
o
);
if
(
err
<
0
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Error setting output stream dispositions
\n
"
);
exit_program
(
1
);
}
// parse forced keyframe specifications;
// must be done after chapters are created
err
=
process_forced_keyframes
(
mux
,
o
);
if
(
err
<
0
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Error processing forced keyframes
\n
"
);
exit_program
(
1
);
}
err
=
setup_sync_queues
(
mux
,
oc
,
o
->
shortest_buf_duration
*
AV_TIME_BASE
);
if
(
err
<
0
)
{
av_log
(
mux
,
AV_LOG_FATAL
,
"Error setting up output sync queues
\n
"
);
exit_program
(
1
);
}
of
->
url
=
filename
;
/* write the header for files with no streams */
if
(
of
->
format
->
flags
&
AVFMT_NOSTREAMS
&&
oc
->
nb_streams
==
0
)
{
int
ret
=
mux_check_init
(
mux
);
if
(
ret
<
0
)
return
ret
;
}
return
0
;
}
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_opt.c
浏览文件 @
f23762b9
This source diff could not be displayed because it is too large. You can
view the blob
instead.
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffprobe.c
浏览文件 @
f23762b9
/*
/*
* Copyright (c) 2007-2010 Stefano Sabatini
* Copyright (c) 2007-2010 Stefano Sabatini
* Copyright (c) 2020 Taner Sener ( tanersener gmail com )
* Copyright (c) 2020-2022 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
*
*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
*
*
...
@@ -29,6 +30,13 @@
...
@@ -29,6 +30,13 @@
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools header names updated
*
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
* mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
*
*
* 09.2022
* 09.2022
...
@@ -52,9 +60,11 @@
...
@@ -52,9 +60,11 @@
#include "libavutil/ffversion.h"
#include "libavutil/ffversion.h"
#include <string.h>
#include <string.h>
#include <math.h>
#include "libavformat/avformat.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavcodec/avcodec.h"
#include "libavutil/ambient_viewing_environment.h"
#include "libavutil/avassert.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "libavutil/bprint.h"
...
@@ -163,6 +173,8 @@ typedef struct ReadInterval {
...
@@ -163,6 +173,8 @@ typedef struct ReadInterval {
__thread
ReadInterval
*
read_intervals
;
__thread
ReadInterval
*
read_intervals
;
__thread
int
read_intervals_nb
=
0
;
__thread
int
read_intervals_nb
=
0
;
__thread
int
find_stream_info
=
1
;
/* section structure definition */
/* section structure definition */
#define SECTION_MAX_NB_CHILDREN 10
#define SECTION_MAX_NB_CHILDREN 10
...
@@ -626,6 +638,7 @@ static inline void writer_put_str_printf(WriterContext *wctx, const char *str)
...
@@ -626,6 +638,7 @@ static inline void writer_put_str_printf(WriterContext *wctx, const char *str)
static
inline
void
writer_printf_printf
(
WriterContext
*
wctx
,
const
char
*
fmt
,
...)
static
inline
void
writer_printf_printf
(
WriterContext
*
wctx
,
const
char
*
fmt
,
...)
{
{
va_list
ap
;
va_list
ap
;
va_start
(
ap
,
fmt
);
va_start
(
ap
,
fmt
);
av_vlog
(
NULL
,
AV_LOG_STDERR
,
fmt
,
ap
);
av_vlog
(
NULL
,
AV_LOG_STDERR
,
fmt
,
ap
);
va_end
(
ap
);
va_end
(
ap
);
...
@@ -671,7 +684,7 @@ static int writer_open(WriterContext **wctx, const Writer *writer, const char *a
...
@@ -671,7 +684,7 @@ static int writer_open(WriterContext **wctx, const Writer *writer, const char *a
goto
fail
;
goto
fail
;
}
}
while
((
opt
=
av_dict_
get
(
opts
,
""
,
opt
,
AV_DICT_IGNORE_SUFFIX
)))
{
while
((
opt
=
av_dict_
iterate
(
opts
,
opt
)))
{
if
((
ret
=
av_opt_set
(
*
wctx
,
opt
->
key
,
opt
->
value
,
AV_OPT_SEARCH_CHILDREN
))
<
0
)
{
if
((
ret
=
av_opt_set
(
*
wctx
,
opt
->
key
,
opt
->
value
,
AV_OPT_SEARCH_CHILDREN
))
<
0
)
{
av_log
(
*
wctx
,
AV_LOG_ERROR
,
"Failed to set option '%s' with value '%s' provided to writer context
\n
"
,
av_log
(
*
wctx
,
AV_LOG_ERROR
,
"Failed to set option '%s' with value '%s' provided to writer context
\n
"
,
opt
->
key
,
opt
->
value
);
opt
->
key
,
opt
->
value
);
...
@@ -1907,12 +1920,14 @@ static void writer_register_all(void)
...
@@ -1907,12 +1920,14 @@ static void writer_register_all(void)
writer_print_string(w, k, pbuf.str, 0); \
writer_print_string(w, k, pbuf.str, 0); \
} while (0)
} while (0)
#define print_list_fmt(k, f, n,
...) do {
\
#define print_list_fmt(k, f, n,
m, ...) do {
\
av_bprint_clear(&pbuf); \
av_bprint_clear(&pbuf); \
for (int idx = 0; idx < n; idx++) { \
for (int idx = 0; idx < n; idx++) { \
if (idx > 0) \
for (int idx2 = 0; idx2 < m; idx2++) { \
av_bprint_chars(&pbuf, ' ', 1); \
if (idx > 0 || idx2 > 0) \
av_bprintf(&pbuf, f, __VA_ARGS__); \
av_bprint_chars(&pbuf, ' ', 1); \
av_bprintf(&pbuf, f, __VA_ARGS__); \
} \
} \
} \
writer_print_string(w, k, pbuf.str, 0); \
writer_print_string(w, k, pbuf.str, 0); \
} while (0)
} while (0)
...
@@ -1953,7 +1968,7 @@ static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id
...
@@ -1953,7 +1968,7 @@ static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id
return
0
;
return
0
;
writer_print_section_header
(
w
,
section_id
);
writer_print_section_header
(
w
,
section_id
);
while
((
tag
=
av_dict_
get
(
tags
,
""
,
tag
,
AV_DICT_IGNORE_SUFFIX
)))
{
while
((
tag
=
av_dict_
iterate
(
tags
,
tag
)))
{
if
((
ret
=
print_str_validate
(
tag
->
key
,
tag
->
value
))
<
0
)
if
((
ret
=
print_str_validate
(
tag
->
key
,
tag
->
value
))
<
0
)
break
;
break
;
}
}
...
@@ -2023,7 +2038,7 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
...
@@ -2023,7 +2038,7 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
const
AVDOVIReshapingCurve
*
curve
=
&
mapping
->
curves
[
c
];
const
AVDOVIReshapingCurve
*
curve
=
&
mapping
->
curves
[
c
];
writer_print_section_header
(
w
,
SECTION_ID_FRAME_SIDE_DATA_COMPONENT
);
writer_print_section_header
(
w
,
SECTION_ID_FRAME_SIDE_DATA_COMPONENT
);
print_list_fmt
(
"pivots"
,
"%"
PRIu16
,
curve
->
num_pivots
,
curve
->
pivots
[
idx
]);
print_list_fmt
(
"pivots"
,
"%"
PRIu16
,
curve
->
num_pivots
,
1
,
curve
->
pivots
[
idx
]);
writer_print_section_header
(
w
,
SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST
);
writer_print_section_header
(
w
,
SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST
);
for
(
int
i
=
0
;
i
<
curve
->
num_pivots
-
1
;
i
++
)
{
for
(
int
i
=
0
;
i
<
curve
->
num_pivots
-
1
;
i
++
)
{
...
@@ -2035,7 +2050,7 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
...
@@ -2035,7 +2050,7 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
print_str
(
"mapping_idc_name"
,
"polynomial"
);
print_str
(
"mapping_idc_name"
,
"polynomial"
);
print_int
(
"poly_order"
,
curve
->
poly_order
[
i
]);
print_int
(
"poly_order"
,
curve
->
poly_order
[
i
]);
print_list_fmt
(
"poly_coef"
,
"%"
PRIi64
,
print_list_fmt
(
"poly_coef"
,
"%"
PRIi64
,
curve
->
poly_order
[
i
]
+
1
,
curve
->
poly_order
[
i
]
+
1
,
1
,
curve
->
poly_coef
[
i
][
idx
]);
curve
->
poly_coef
[
i
][
idx
]);
break
;
break
;
case
AV_DOVI_MAPPING_MMR
:
case
AV_DOVI_MAPPING_MMR
:
...
@@ -2043,8 +2058,8 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
...
@@ -2043,8 +2058,8 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
print_int
(
"mmr_order"
,
curve
->
mmr_order
[
i
]);
print_int
(
"mmr_order"
,
curve
->
mmr_order
[
i
]);
print_int
(
"mmr_constant"
,
curve
->
mmr_constant
[
i
]);
print_int
(
"mmr_constant"
,
curve
->
mmr_constant
[
i
]);
print_list_fmt
(
"mmr_coef"
,
"%"
PRIi64
,
print_list_fmt
(
"mmr_coef"
,
"%"
PRIi64
,
curve
->
mmr_order
[
i
]
*
7
,
curve
->
mmr_order
[
i
]
,
7
,
curve
->
mmr_coef
[
i
][
0
][
idx
]);
curve
->
mmr_coef
[
i
][
idx
][
idx2
]);
break
;
break
;
default:
default:
print_str
(
"mapping_idc_name"
,
"unknown"
);
print_str
(
"mapping_idc_name"
,
"unknown"
);
...
@@ -2082,15 +2097,15 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
...
@@ -2082,15 +2097,15 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
print_int
(
"dm_metadata_id"
,
color
->
dm_metadata_id
);
print_int
(
"dm_metadata_id"
,
color
->
dm_metadata_id
);
print_int
(
"scene_refresh_flag"
,
color
->
scene_refresh_flag
);
print_int
(
"scene_refresh_flag"
,
color
->
scene_refresh_flag
);
print_list_fmt
(
"ycc_to_rgb_matrix"
,
"%d/%d"
,
print_list_fmt
(
"ycc_to_rgb_matrix"
,
"%d/%d"
,
FF_ARRAY_ELEMS
(
color
->
ycc_to_rgb_matrix
),
FF_ARRAY_ELEMS
(
color
->
ycc_to_rgb_matrix
),
1
,
color
->
ycc_to_rgb_matrix
[
idx
].
num
,
color
->
ycc_to_rgb_matrix
[
idx
].
num
,
color
->
ycc_to_rgb_matrix
[
idx
].
den
);
color
->
ycc_to_rgb_matrix
[
idx
].
den
);
print_list_fmt
(
"ycc_to_rgb_offset"
,
"%d/%d"
,
print_list_fmt
(
"ycc_to_rgb_offset"
,
"%d/%d"
,
FF_ARRAY_ELEMS
(
color
->
ycc_to_rgb_offset
),
FF_ARRAY_ELEMS
(
color
->
ycc_to_rgb_offset
),
1
,
color
->
ycc_to_rgb_offset
[
idx
].
num
,
color
->
ycc_to_rgb_offset
[
idx
].
num
,
color
->
ycc_to_rgb_offset
[
idx
].
den
);
color
->
ycc_to_rgb_offset
[
idx
].
den
);
print_list_fmt
(
"rgb_to_lms_matrix"
,
"%d/%d"
,
print_list_fmt
(
"rgb_to_lms_matrix"
,
"%d/%d"
,
FF_ARRAY_ELEMS
(
color
->
rgb_to_lms_matrix
),
FF_ARRAY_ELEMS
(
color
->
rgb_to_lms_matrix
),
1
,
color
->
rgb_to_lms_matrix
[
idx
].
num
,
color
->
rgb_to_lms_matrix
[
idx
].
num
,
color
->
rgb_to_lms_matrix
[
idx
].
den
);
color
->
rgb_to_lms_matrix
[
idx
].
den
);
print_int
(
"signal_eotf"
,
color
->
signal_eotf
);
print_int
(
"signal_eotf"
,
color
->
signal_eotf
);
...
@@ -2276,6 +2291,17 @@ static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *m
...
@@ -2276,6 +2291,17 @@ static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *m
}
}
}
}
static
void
print_ambient_viewing_environment
(
WriterContext
*
w
,
const
AVAmbientViewingEnvironment
*
env
)
{
if
(
!
env
)
return
;
print_q
(
"ambient_illuminance"
,
env
->
ambient_illuminance
,
'/'
);
print_q
(
"ambient_light_x"
,
env
->
ambient_light_x
,
'/'
);
print_q
(
"ambient_light_y"
,
env
->
ambient_light_y
,
'/'
);
}
static
void
print_pkt_side_data
(
WriterContext
*
w
,
static
void
print_pkt_side_data
(
WriterContext
*
w
,
AVCodecParameters
*
par
,
AVCodecParameters
*
par
,
const
AVPacketSideData
*
side_data
,
const
AVPacketSideData
*
side_data
,
...
@@ -2293,8 +2319,11 @@ static void print_pkt_side_data(WriterContext *w,
...
@@ -2293,8 +2319,11 @@ static void print_pkt_side_data(WriterContext *w,
writer_print_section_header
(
w
,
id_data
);
writer_print_section_header
(
w
,
id_data
);
print_str
(
"side_data_type"
,
name
?
name
:
"unknown"
);
print_str
(
"side_data_type"
,
name
?
name
:
"unknown"
);
if
(
sd
->
type
==
AV_PKT_DATA_DISPLAYMATRIX
&&
sd
->
size
>=
9
*
4
)
{
if
(
sd
->
type
==
AV_PKT_DATA_DISPLAYMATRIX
&&
sd
->
size
>=
9
*
4
)
{
double
rotation
=
av_display_rotation_get
((
int32_t
*
)
sd
->
data
);
if
(
isnan
(
rotation
))
rotation
=
0
;
writer_print_integers
(
w
,
"displaymatrix"
,
sd
->
data
,
9
,
" %11d"
,
3
,
4
,
1
);
writer_print_integers
(
w
,
"displaymatrix"
,
sd
->
data
,
9
,
" %11d"
,
3
,
4
,
1
);
print_int
(
"rotation"
,
av_display_rotation_get
((
int32_t
*
)
sd
->
data
)
);
print_int
(
"rotation"
,
rotation
);
}
else
if
(
sd
->
type
==
AV_PKT_DATA_STEREO3D
)
{
}
else
if
(
sd
->
type
==
AV_PKT_DATA_STEREO3D
)
{
const
AVStereo3D
*
stereo
=
(
AVStereo3D
*
)
sd
->
data
;
const
AVStereo3D
*
stereo
=
(
AVStereo3D
*
)
sd
->
data
;
print_str
(
"type"
,
av_stereo3d_type_name
(
stereo
->
type
));
print_str
(
"type"
,
av_stereo3d_type_name
(
stereo
->
type
));
...
@@ -2506,8 +2535,12 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
...
@@ -2506,8 +2535,12 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
print_val
(
"size"
,
pkt
->
size
,
unit_byte_str
);
print_val
(
"size"
,
pkt
->
size
,
unit_byte_str
);
if
(
pkt
->
pos
!=
-
1
)
print_fmt
(
"pos"
,
"%"
PRId64
,
pkt
->
pos
);
if
(
pkt
->
pos
!=
-
1
)
print_fmt
(
"pos"
,
"%"
PRId64
,
pkt
->
pos
);
else
print_str_opt
(
"pos"
,
"N/A"
);
else
print_str_opt
(
"pos"
,
"N/A"
);
print_fmt
(
"flags"
,
"%c%c"
,
pkt
->
flags
&
AV_PKT_FLAG_KEY
?
'K'
:
'_'
,
print_fmt
(
"flags"
,
"%c%c%c"
,
pkt
->
flags
&
AV_PKT_FLAG_KEY
?
'K'
:
'_'
,
pkt
->
flags
&
AV_PKT_FLAG_DISCARD
?
'D'
:
'_'
);
pkt
->
flags
&
AV_PKT_FLAG_DISCARD
?
'D'
:
'_'
,
pkt
->
flags
&
AV_PKT_FLAG_CORRUPT
?
'C'
:
'_'
);
if
(
do_show_data
)
writer_print_data
(
w
,
"data"
,
pkt
->
data
,
pkt
->
size
);
writer_print_data_hash
(
w
,
"data_hash"
,
pkt
->
data
,
pkt
->
size
);
if
(
pkt
->
side_data_elems
)
{
if
(
pkt
->
side_data_elems
)
{
size_t
size
;
size_t
size
;
...
@@ -2526,9 +2559,6 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
...
@@ -2526,9 +2559,6 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
SECTION_ID_PACKET_SIDE_DATA
);
SECTION_ID_PACKET_SIDE_DATA
);
}
}
if
(
do_show_data
)
writer_print_data
(
w
,
"data"
,
pkt
->
data
,
pkt
->
size
);
writer_print_data_hash
(
w
,
"data_hash"
,
pkt
->
data
,
pkt
->
size
);
writer_print_section_footer
(
w
);
writer_print_section_footer
(
w
);
av_bprint_finalize
(
&
pbuf
,
NULL
);
av_bprint_finalize
(
&
pbuf
,
NULL
);
...
@@ -2581,8 +2611,14 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
...
@@ -2581,8 +2611,14 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
print_time
(
"pkt_dts_time"
,
frame
->
pkt_dts
,
&
stream
->
time_base
);
print_time
(
"pkt_dts_time"
,
frame
->
pkt_dts
,
&
stream
->
time_base
);
print_ts
(
"best_effort_timestamp"
,
frame
->
best_effort_timestamp
);
print_ts
(
"best_effort_timestamp"
,
frame
->
best_effort_timestamp
);
print_time
(
"best_effort_timestamp_time"
,
frame
->
best_effort_timestamp
,
&
stream
->
time_base
);
print_time
(
"best_effort_timestamp_time"
,
frame
->
best_effort_timestamp
,
&
stream
->
time_base
);
#if LIBAVUTIL_VERSION_MAJOR < 59
AV_NOWARN_DEPRECATED
(
print_duration_ts
(
"pkt_duration"
,
frame
->
pkt_duration
);
print_duration_ts
(
"pkt_duration"
,
frame
->
pkt_duration
);
print_duration_time
(
"pkt_duration_time"
,
frame
->
pkt_duration
,
&
stream
->
time_base
);
print_duration_time
(
"pkt_duration_time"
,
frame
->
pkt_duration
,
&
stream
->
time_base
);
)
#endif
print_duration_ts
(
"duration"
,
frame
->
duration
);
print_duration_time
(
"duration_time"
,
frame
->
duration
,
&
stream
->
time_base
);
if
(
frame
->
pkt_pos
!=
-
1
)
print_fmt
(
"pkt_pos"
,
"%"
PRId64
,
frame
->
pkt_pos
);
if
(
frame
->
pkt_pos
!=
-
1
)
print_fmt
(
"pkt_pos"
,
"%"
PRId64
,
frame
->
pkt_pos
);
else
print_str_opt
(
"pkt_pos"
,
"N/A"
);
else
print_str_opt
(
"pkt_pos"
,
"N/A"
);
if
(
frame
->
pkt_size
!=
-
1
)
print_val
(
"pkt_size"
,
frame
->
pkt_size
,
unit_byte_str
);
if
(
frame
->
pkt_size
!=
-
1
)
print_val
(
"pkt_size"
,
frame
->
pkt_size
,
unit_byte_str
);
...
@@ -2604,8 +2640,12 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
...
@@ -2604,8 +2640,12 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
print_str_opt
(
"sample_aspect_ratio"
,
"N/A"
);
print_str_opt
(
"sample_aspect_ratio"
,
"N/A"
);
}
}
print_fmt
(
"pict_type"
,
"%c"
,
av_get_picture_type_char
(
frame
->
pict_type
));
print_fmt
(
"pict_type"
,
"%c"
,
av_get_picture_type_char
(
frame
->
pict_type
));
#if LIBAVUTIL_VERSION_MAJOR < 59
AV_NOWARN_DEPRECATED
(
print_int
(
"coded_picture_number"
,
frame
->
coded_picture_number
);
print_int
(
"coded_picture_number"
,
frame
->
coded_picture_number
);
print_int
(
"display_picture_number"
,
frame
->
display_picture_number
);
print_int
(
"display_picture_number"
,
frame
->
display_picture_number
);
)
#endif
print_int
(
"interlaced_frame"
,
frame
->
interlaced_frame
);
print_int
(
"interlaced_frame"
,
frame
->
interlaced_frame
);
print_int
(
"top_field_first"
,
frame
->
top_field_first
);
print_int
(
"top_field_first"
,
frame
->
top_field_first
);
print_int
(
"repeat_pict"
,
frame
->
repeat_pict
);
print_int
(
"repeat_pict"
,
frame
->
repeat_pict
);
...
@@ -2644,8 +2684,11 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
...
@@ -2644,8 +2684,11 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
name
=
av_frame_side_data_name
(
sd
->
type
);
name
=
av_frame_side_data_name
(
sd
->
type
);
print_str
(
"side_data_type"
,
name
?
name
:
"unknown"
);
print_str
(
"side_data_type"
,
name
?
name
:
"unknown"
);
if
(
sd
->
type
==
AV_FRAME_DATA_DISPLAYMATRIX
&&
sd
->
size
>=
9
*
4
)
{
if
(
sd
->
type
==
AV_FRAME_DATA_DISPLAYMATRIX
&&
sd
->
size
>=
9
*
4
)
{
double
rotation
=
av_display_rotation_get
((
int32_t
*
)
sd
->
data
);
if
(
isnan
(
rotation
))
rotation
=
0
;
writer_print_integers
(
w
,
"displaymatrix"
,
sd
->
data
,
9
,
" %11d"
,
3
,
4
,
1
);
writer_print_integers
(
w
,
"displaymatrix"
,
sd
->
data
,
9
,
" %11d"
,
3
,
4
,
1
);
print_int
(
"rotation"
,
av_display_rotation_get
((
int32_t
*
)
sd
->
data
)
);
print_int
(
"rotation"
,
rotation
);
}
else
if
(
sd
->
type
==
AV_FRAME_DATA_AFD
&&
sd
->
size
>
0
)
{
}
else
if
(
sd
->
type
==
AV_FRAME_DATA_AFD
&&
sd
->
size
>
0
)
{
print_int
(
"active_format"
,
*
sd
->
data
);
print_int
(
"active_format"
,
*
sd
->
data
);
}
else
if
(
sd
->
type
==
AV_FRAME_DATA_GOP_TIMECODE
&&
sd
->
size
>=
8
)
{
}
else
if
(
sd
->
type
==
AV_FRAME_DATA_GOP_TIMECODE
&&
sd
->
size
>=
8
)
{
...
@@ -2700,6 +2743,9 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
...
@@ -2700,6 +2743,9 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
}
else
if
(
sd
->
type
==
AV_FRAME_DATA_DYNAMIC_HDR_VIVID
)
{
}
else
if
(
sd
->
type
==
AV_FRAME_DATA_DYNAMIC_HDR_VIVID
)
{
AVDynamicHDRVivid
*
metadata
=
(
AVDynamicHDRVivid
*
)
sd
->
data
;
AVDynamicHDRVivid
*
metadata
=
(
AVDynamicHDRVivid
*
)
sd
->
data
;
print_dynamic_hdr_vivid
(
w
,
metadata
);
print_dynamic_hdr_vivid
(
w
,
metadata
);
}
else
if
(
sd
->
type
==
AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT
)
{
print_ambient_viewing_environment
(
w
,
(
const
AVAmbientViewingEnvironment
*
)
sd
->
data
);
}
}
writer_print_section_footer
(
w
);
writer_print_section_footer
(
w
);
}
}
...
@@ -2714,7 +2760,7 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
...
@@ -2714,7 +2760,7 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
static
av_always_inline
int
process_frame
(
WriterContext
*
w
,
static
av_always_inline
int
process_frame
(
WriterContext
*
w
,
InputFile
*
ifile
,
InputFile
*
ifile
,
AVFrame
*
frame
,
AVPacket
*
pkt
,
AVFrame
*
frame
,
const
AVPacket
*
pkt
,
int
*
packet_new
)
int
*
packet_new
)
{
{
AVFormatContext
*
fmt_ctx
=
ifile
->
fmt_ctx
;
AVFormatContext
*
fmt_ctx
=
ifile
->
fmt_ctx
;
...
@@ -2858,9 +2904,10 @@ static int read_interval_packets(WriterContext *w, InputFile *ifile,
...
@@ -2858,9 +2904,10 @@ static int read_interval_packets(WriterContext *w, InputFile *ifile,
}
}
if
(
selected_streams
[
pkt
->
stream_index
])
{
if
(
selected_streams
[
pkt
->
stream_index
])
{
AVRational
tb
=
ifile
->
streams
[
pkt
->
stream_index
].
st
->
time_base
;
AVRational
tb
=
ifile
->
streams
[
pkt
->
stream_index
].
st
->
time_base
;
int64_t
pts
=
pkt
->
pts
!=
AV_NOPTS_VALUE
?
pkt
->
pts
:
pkt
->
dts
;
if
(
p
kt
->
p
ts
!=
AV_NOPTS_VALUE
)
if
(
pts
!=
AV_NOPTS_VALUE
)
*
cur_ts
=
av_rescale_q
(
p
kt
->
p
ts
,
tb
,
AV_TIME_BASE_Q
);
*
cur_ts
=
av_rescale_q
(
pts
,
tb
,
AV_TIME_BASE_Q
);
if
(
!
has_start
&&
*
cur_ts
!=
AV_NOPTS_VALUE
)
{
if
(
!
has_start
&&
*
cur_ts
!=
AV_NOPTS_VALUE
)
{
start
=
*
cur_ts
;
start
=
*
cur_ts
;
...
@@ -2894,7 +2941,7 @@ static int read_interval_packets(WriterContext *w, InputFile *ifile,
...
@@ -2894,7 +2941,7 @@ static int read_interval_packets(WriterContext *w, InputFile *ifile,
}
}
av_packet_unref
(
pkt
);
av_packet_unref
(
pkt
);
//Flush remaining frames that are cached in the decoder
//Flush remaining frames that are cached in the decoder
for
(
i
=
0
;
i
<
fmt_ctx
->
nb_streams
;
i
++
)
{
for
(
i
=
0
;
i
<
ifile
->
nb_streams
;
i
++
)
{
pkt
->
stream_index
=
i
;
pkt
->
stream_index
=
i
;
if
(
do_read_frames
)
{
if
(
do_read_frames
)
{
while
(
process_frame
(
w
,
ifile
,
frame
,
pkt
,
&
(
int
){
1
})
>
0
);
while
(
process_frame
(
w
,
ifile
,
frame
,
pkt
,
&
(
int
){
1
})
>
0
);
...
@@ -3052,6 +3099,8 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id
...
@@ -3052,6 +3099,8 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id
}
}
print_int
(
"bits_per_sample"
,
av_get_bits_per_sample
(
par
->
codec_id
));
print_int
(
"bits_per_sample"
,
av_get_bits_per_sample
(
par
->
codec_id
));
print_int
(
"initial_padding"
,
par
->
initial_padding
);
break
;
break
;
case
AVMEDIA_TYPE_SUBTITLE
:
case
AVMEDIA_TYPE_SUBTITLE
:
...
@@ -3278,15 +3327,9 @@ static int show_format(WriterContext *w, InputFile *ifile)
...
@@ -3278,15 +3327,9 @@ static int show_format(WriterContext *w, InputFile *ifile)
static
void
show_error
(
WriterContext
*
w
,
int
err
)
static
void
show_error
(
WriterContext
*
w
,
int
err
)
{
{
char
errbuf
[
128
];
const
char
*
errbuf_ptr
=
errbuf
;
if
(
av_strerror
(
err
,
errbuf
,
sizeof
(
errbuf
))
<
0
)
errbuf_ptr
=
strerror
(
AVUNERROR
(
err
));
writer_print_section_header
(
w
,
SECTION_ID_ERROR
);
writer_print_section_header
(
w
,
SECTION_ID_ERROR
);
print_int
(
"code"
,
err
);
print_int
(
"code"
,
err
);
print_str
(
"string"
,
errbuf_ptr
);
print_str
(
"string"
,
av_err2str
(
err
)
);
writer_print_section_footer
(
w
);
writer_print_section_footer
(
w
);
}
}
...
@@ -3299,10 +3342,8 @@ static int open_input_file(InputFile *ifile, const char *filename,
...
@@ -3299,10 +3342,8 @@ static int open_input_file(InputFile *ifile, const char *filename,
int
scan_all_pmts_set
=
0
;
int
scan_all_pmts_set
=
0
;
fmt_ctx
=
avformat_alloc_context
();
fmt_ctx
=
avformat_alloc_context
();
if
(
!
fmt_ctx
)
{
if
(
!
fmt_ctx
)
print_error
(
filename
,
AVERROR
(
ENOMEM
));
report_and_exit
(
AVERROR
(
ENOMEM
));
exit_program
(
1
);
}
if
(
!
av_dict_get
(
format_opts
,
"scan_all_pmts"
,
NULL
,
AV_DICT_MATCH_CASE
))
{
if
(
!
av_dict_get
(
format_opts
,
"scan_all_pmts"
,
NULL
,
AV_DICT_MATCH_CASE
))
{
av_dict_set
(
&
format_opts
,
"scan_all_pmts"
,
"1"
,
AV_DICT_DONT_OVERWRITE
);
av_dict_set
(
&
format_opts
,
"scan_all_pmts"
,
"1"
,
AV_DICT_DONT_OVERWRITE
);
...
@@ -3320,7 +3361,7 @@ static int open_input_file(InputFile *ifile, const char *filename,
...
@@ -3320,7 +3361,7 @@ static int open_input_file(InputFile *ifile, const char *filename,
ifile
->
fmt_ctx
=
fmt_ctx
;
ifile
->
fmt_ctx
=
fmt_ctx
;
if
(
scan_all_pmts_set
)
if
(
scan_all_pmts_set
)
av_dict_set
(
&
format_opts
,
"scan_all_pmts"
,
NULL
,
AV_DICT_MATCH_CASE
);
av_dict_set
(
&
format_opts
,
"scan_all_pmts"
,
NULL
,
AV_DICT_MATCH_CASE
);
while
((
t
=
av_dict_
get
(
format_opts
,
""
,
t
,
AV_DICT_IGNORE_SUFFIX
)))
while
((
t
=
av_dict_
iterate
(
format_opts
,
t
)))
av_log
(
NULL
,
AV_LOG_WARNING
,
"Option %s skipped - not known to demuxer.
\n
"
,
t
->
key
);
av_log
(
NULL
,
AV_LOG_WARNING
,
"Option %s skipped - not known to demuxer.
\n
"
,
t
->
key
);
if
(
find_stream_info
)
{
if
(
find_stream_info
)
{
...
@@ -3718,7 +3759,7 @@ static void opt_input_file(void *optctx, const char *arg)
...
@@ -3718,7 +3759,7 @@ static void opt_input_file(void *optctx, const char *arg)
exit_program
(
1
);
exit_program
(
1
);
}
}
if
(
!
strcmp
(
arg
,
"-"
))
if
(
!
strcmp
(
arg
,
"-"
))
arg
=
"
pipe
:"
;
arg
=
"
fd
:"
;
input_filename
=
arg
;
input_filename
=
arg
;
}
}
...
@@ -3737,7 +3778,7 @@ static void opt_output_file(void *optctx, const char *arg)
...
@@ -3737,7 +3778,7 @@ static void opt_output_file(void *optctx, const char *arg)
exit_program
(
1
);
exit_program
(
1
);
}
}
if
(
!
strcmp
(
arg
,
"-"
))
if
(
!
strcmp
(
arg
,
"-"
))
arg
=
"
pipe
:"
;
arg
=
"
fd
:"
;
output_filename
=
arg
;
output_filename
=
arg
;
}
}
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.c
0 → 100644
浏览文件 @
f23762b9
/*
* This file is part of FFmpeg.
* Copyright (c) 2023 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of objpool.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools header names updated
*/
#include <stdint.h>
#include "libavcodec/packet.h"
#include "libavutil/common.h"
#include "libavutil/error.h"
#include "libavutil/frame.h"
#include "libavutil/mem.h"
#include "fftools_objpool.h"
struct
ObjPool
{
void
*
pool
[
32
];
unsigned
int
pool_count
;
ObjPoolCBAlloc
alloc
;
ObjPoolCBReset
reset
;
ObjPoolCBFree
free
;
};
ObjPool
*
objpool_alloc
(
ObjPoolCBAlloc
cb_alloc
,
ObjPoolCBReset
cb_reset
,
ObjPoolCBFree
cb_free
)
{
ObjPool
*
op
=
av_mallocz
(
sizeof
(
*
op
));
if
(
!
op
)
return
NULL
;
op
->
alloc
=
cb_alloc
;
op
->
reset
=
cb_reset
;
op
->
free
=
cb_free
;
return
op
;
}
void
objpool_free
(
ObjPool
**
pop
)
{
ObjPool
*
op
=
*
pop
;
if
(
!
op
)
return
;
for
(
unsigned
int
i
=
0
;
i
<
op
->
pool_count
;
i
++
)
op
->
free
(
&
op
->
pool
[
i
]);
av_freep
(
pop
);
}
int
objpool_get
(
ObjPool
*
op
,
void
**
obj
)
{
if
(
op
->
pool_count
)
{
*
obj
=
op
->
pool
[
--
op
->
pool_count
];
op
->
pool
[
op
->
pool_count
]
=
NULL
;
}
else
*
obj
=
op
->
alloc
();
return
*
obj
?
0
:
AVERROR
(
ENOMEM
);
}
void
objpool_release
(
ObjPool
*
op
,
void
**
obj
)
{
if
(
!*
obj
)
return
;
op
->
reset
(
*
obj
);
if
(
op
->
pool_count
<
FF_ARRAY_ELEMS
(
op
->
pool
))
op
->
pool
[
op
->
pool_count
++
]
=
*
obj
;
else
op
->
free
(
obj
);
*
obj
=
NULL
;
}
static
void
*
alloc_packet
(
void
)
{
return
av_packet_alloc
();
}
static
void
*
alloc_frame
(
void
)
{
return
av_frame_alloc
();
}
static
void
reset_packet
(
void
*
obj
)
{
av_packet_unref
(
obj
);
}
static
void
reset_frame
(
void
*
obj
)
{
av_frame_unref
(
obj
);
}
static
void
free_packet
(
void
**
obj
)
{
AVPacket
*
pkt
=
*
obj
;
av_packet_free
(
&
pkt
);
*
obj
=
NULL
;
}
static
void
free_frame
(
void
**
obj
)
{
AVFrame
*
frame
=
*
obj
;
av_frame_free
(
&
frame
);
*
obj
=
NULL
;
}
ObjPool
*
objpool_alloc_packets
(
void
)
{
return
objpool_alloc
(
alloc_packet
,
reset_packet
,
free_packet
);
}
ObjPool
*
objpool_alloc_frames
(
void
)
{
return
objpool_alloc
(
alloc_frame
,
reset_frame
,
free_frame
);
}
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.h
0 → 100644
浏览文件 @
f23762b9
/*
* This file is part of FFmpeg.
* Copyright (c) 2023 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of objpool.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
*/
#ifndef FFTOOLS_OBJPOOL_H
#define FFTOOLS_OBJPOOL_H
typedef
struct
ObjPool
ObjPool
;
typedef
void
*
(
*
ObjPoolCBAlloc
)(
void
);
typedef
void
(
*
ObjPoolCBReset
)(
void
*
);
typedef
void
(
*
ObjPoolCBFree
)(
void
**
);
void
objpool_free
(
ObjPool
**
op
);
ObjPool
*
objpool_alloc
(
ObjPoolCBAlloc
cb_alloc
,
ObjPoolCBReset
cb_reset
,
ObjPoolCBFree
cb_free
);
ObjPool
*
objpool_alloc_packets
(
void
);
ObjPool
*
objpool_alloc_frames
(
void
);
int
objpool_get
(
ObjPool
*
op
,
void
**
obj
);
void
objpool_release
(
ObjPool
*
op
,
void
**
obj
);
#endif // FFTOOLS_OBJPOOL_H
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.c
浏览文件 @
f23762b9
/*
/*
* Option handlers shared between the tools.
* Option handlers shared between the tools.
* copyright (c) 2022 Taner Sener ( tanersener gmail com )
* Copyright (c) 2022 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
*
*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
*
*
...
@@ -24,6 +25,12 @@
...
@@ -24,6 +25,12 @@
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop the ffmpeg-kit library.
* by us to develop the ffmpeg-kit library.
*
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - time field in report_callback updated as double
*
* ffmpeg-kit changes by Taner Sener
* ffmpeg-kit changes by Taner Sener
*
*
* 09.2022
* 09.2022
...
@@ -88,7 +95,7 @@ static __thread FILE *report_file = NULL;
...
@@ -88,7 +95,7 @@ static __thread FILE *report_file = NULL;
static
__thread
int
report_file_level
=
AV_LOG_DEBUG
;
static
__thread
int
report_file_level
=
AV_LOG_DEBUG
;
extern
void
ffmpegkit_log_callback_function
(
void
*
ptr
,
int
level
,
const
char
*
format
,
va_list
vargs
);
extern
void
ffmpegkit_log_callback_function
(
void
*
ptr
,
int
level
,
const
char
*
format
,
va_list
vargs
);
extern
void
(
*
report_callback
)(
int
,
float
,
float
,
int64_t
,
int
,
double
,
double
);
extern
void
(
*
report_callback
)(
int
,
float
,
float
,
int64_t
,
double
,
double
,
double
);
extern
__thread
char
*
program_name
;
extern
__thread
char
*
program_name
;
int
show_license
(
void
*
optctx
,
const
char
*
opt
,
const
char
*
arg
)
int
show_license
(
void
*
optctx
,
const
char
*
opt
,
const
char
*
arg
)
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.h
浏览文件 @
f23762b9
/*
/*
* Option handlers shared between the tools.
* Option handlers shared between the tools.
*
copyright (c) 2022 Taner Sener ( tanersener gmail com )
*
Copyright (c) 2022 Taner Sener
*
*
* This file is part of FFmpeg.
* This file is part of FFmpeg.
*
*
...
...
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.c
0 → 100644
浏览文件 @
f23762b9
/*
* This file is part of FFmpeg.
* Copyright (c) 2023 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of sync_queue.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools header names updated
*/
#include <stdint.h>
#include <string.h>
#include "libavutil/avassert.h"
#include "libavutil/error.h"
#include "libavutil/fifo.h"
#include "libavutil/mathematics.h"
#include "libavutil/mem.h"
#include "fftools_objpool.h"
#include "fftools_sync_queue.h"
typedef
struct
SyncQueueStream
{
AVFifo
*
fifo
;
AVRational
tb
;
/* stream head: largest timestamp seen */
int64_t
head_ts
;
int
limiting
;
/* no more frames will be sent for this stream */
int
finished
;
uint64_t
frames_sent
;
uint64_t
frames_max
;
}
SyncQueueStream
;
struct
SyncQueue
{
enum
SyncQueueType
type
;
/* no more frames will be sent for any stream */
int
finished
;
/* sync head: the stream with the _smallest_ head timestamp
* this stream determines which frames can be output */
int
head_stream
;
/* the finished stream with the smallest finish timestamp or -1 */
int
head_finished_stream
;
// maximum buffering duration in microseconds
int64_t
buf_size_us
;
SyncQueueStream
*
streams
;
unsigned
int
nb_streams
;
// pool of preallocated frames to avoid constant allocations
ObjPool
*
pool
;
};
static
void
frame_move
(
const
SyncQueue
*
sq
,
SyncQueueFrame
dst
,
SyncQueueFrame
src
)
{
if
(
sq
->
type
==
SYNC_QUEUE_PACKETS
)
av_packet_move_ref
(
dst
.
p
,
src
.
p
);
else
av_frame_move_ref
(
dst
.
f
,
src
.
f
);
}
static
int64_t
frame_ts
(
const
SyncQueue
*
sq
,
SyncQueueFrame
frame
)
{
return
(
sq
->
type
==
SYNC_QUEUE_PACKETS
)
?
frame
.
p
->
pts
+
frame
.
p
->
duration
:
frame
.
f
->
pts
+
frame
.
f
->
duration
;
}
static
int
frame_null
(
const
SyncQueue
*
sq
,
SyncQueueFrame
frame
)
{
return
(
sq
->
type
==
SYNC_QUEUE_PACKETS
)
?
(
frame
.
p
==
NULL
)
:
(
frame
.
f
==
NULL
);
}
static
void
finish_stream
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
)
{
SyncQueueStream
*
st
=
&
sq
->
streams
[
stream_idx
];
st
->
finished
=
1
;
if
(
st
->
limiting
&&
st
->
head_ts
!=
AV_NOPTS_VALUE
)
{
/* check if this stream is the new finished head */
if
(
sq
->
head_finished_stream
<
0
||
av_compare_ts
(
st
->
head_ts
,
st
->
tb
,
sq
->
streams
[
sq
->
head_finished_stream
].
head_ts
,
sq
->
streams
[
sq
->
head_finished_stream
].
tb
)
<
0
)
{
sq
->
head_finished_stream
=
stream_idx
;
}
/* mark as finished all streams that should no longer receive new frames,
* due to them being ahead of some finished stream */
st
=
&
sq
->
streams
[
sq
->
head_finished_stream
];
for
(
unsigned
int
i
=
0
;
i
<
sq
->
nb_streams
;
i
++
)
{
SyncQueueStream
*
st1
=
&
sq
->
streams
[
i
];
if
(
st
!=
st1
&&
st1
->
head_ts
!=
AV_NOPTS_VALUE
&&
av_compare_ts
(
st
->
head_ts
,
st
->
tb
,
st1
->
head_ts
,
st1
->
tb
)
<=
0
)
st1
->
finished
=
1
;
}
}
/* mark the whole queue as finished if all streams are finished */
for
(
unsigned
int
i
=
0
;
i
<
sq
->
nb_streams
;
i
++
)
{
if
(
!
sq
->
streams
[
i
].
finished
)
return
;
}
sq
->
finished
=
1
;
}
static
void
queue_head_update
(
SyncQueue
*
sq
)
{
if
(
sq
->
head_stream
<
0
)
{
/* wait for one timestamp in each stream before determining
* the queue head */
for
(
unsigned
int
i
=
0
;
i
<
sq
->
nb_streams
;
i
++
)
{
SyncQueueStream
*
st
=
&
sq
->
streams
[
i
];
if
(
st
->
limiting
&&
st
->
head_ts
==
AV_NOPTS_VALUE
)
return
;
}
// placeholder value, correct one will be found below
sq
->
head_stream
=
0
;
}
for
(
unsigned
int
i
=
0
;
i
<
sq
->
nb_streams
;
i
++
)
{
SyncQueueStream
*
st_head
=
&
sq
->
streams
[
sq
->
head_stream
];
SyncQueueStream
*
st_other
=
&
sq
->
streams
[
i
];
if
(
st_other
->
limiting
&&
st_other
->
head_ts
!=
AV_NOPTS_VALUE
&&
av_compare_ts
(
st_other
->
head_ts
,
st_other
->
tb
,
st_head
->
head_ts
,
st_head
->
tb
)
<
0
)
sq
->
head_stream
=
i
;
}
}
/* update this stream's head timestamp */
static
void
stream_update_ts
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
,
int64_t
ts
)
{
SyncQueueStream
*
st
=
&
sq
->
streams
[
stream_idx
];
if
(
ts
==
AV_NOPTS_VALUE
||
(
st
->
head_ts
!=
AV_NOPTS_VALUE
&&
st
->
head_ts
>=
ts
))
return
;
st
->
head_ts
=
ts
;
/* if this stream is now ahead of some finished stream, then
* this stream is also finished */
if
(
sq
->
head_finished_stream
>=
0
&&
av_compare_ts
(
sq
->
streams
[
sq
->
head_finished_stream
].
head_ts
,
sq
->
streams
[
sq
->
head_finished_stream
].
tb
,
ts
,
st
->
tb
)
<=
0
)
finish_stream
(
sq
,
stream_idx
);
/* update the overall head timestamp if it could have changed */
if
(
st
->
limiting
&&
(
sq
->
head_stream
<
0
||
sq
->
head_stream
==
stream_idx
))
queue_head_update
(
sq
);
}
/* If the queue for the given stream (or all streams when stream_idx=-1)
* is overflowing, trigger a fake heartbeat on lagging streams.
*
* @return 1 if heartbeat triggered, 0 otherwise
*/
static
int
overflow_heartbeat
(
SyncQueue
*
sq
,
int
stream_idx
)
{
SyncQueueStream
*
st
;
SyncQueueFrame
frame
;
int64_t
tail_ts
=
AV_NOPTS_VALUE
;
/* if no stream specified, pick the one that is most ahead */
if
(
stream_idx
<
0
)
{
int64_t
ts
=
AV_NOPTS_VALUE
;
for
(
int
i
=
0
;
i
<
sq
->
nb_streams
;
i
++
)
{
st
=
&
sq
->
streams
[
i
];
if
(
st
->
head_ts
!=
AV_NOPTS_VALUE
&&
(
ts
==
AV_NOPTS_VALUE
||
av_compare_ts
(
ts
,
sq
->
streams
[
stream_idx
].
tb
,
st
->
head_ts
,
st
->
tb
)
<
0
))
{
ts
=
st
->
head_ts
;
stream_idx
=
i
;
}
}
/* no stream has a timestamp yet -> nothing to do */
if
(
stream_idx
<
0
)
return
0
;
}
st
=
&
sq
->
streams
[
stream_idx
];
/* get the chosen stream's tail timestamp */
for
(
size_t
i
=
0
;
tail_ts
==
AV_NOPTS_VALUE
&&
av_fifo_peek
(
st
->
fifo
,
&
frame
,
1
,
i
)
>=
0
;
i
++
)
tail_ts
=
frame_ts
(
sq
,
frame
);
/* overflow triggers when the tail is over specified duration behind the head */
if
(
tail_ts
==
AV_NOPTS_VALUE
||
tail_ts
>=
st
->
head_ts
||
av_rescale_q
(
st
->
head_ts
-
tail_ts
,
st
->
tb
,
AV_TIME_BASE_Q
)
<
sq
->
buf_size_us
)
return
0
;
/* signal a fake timestamp for all streams that prevent tail_ts from being output */
tail_ts
++
;
for
(
unsigned
int
i
=
0
;
i
<
sq
->
nb_streams
;
i
++
)
{
SyncQueueStream
*
st1
=
&
sq
->
streams
[
i
];
int64_t
ts
;
if
(
st
==
st1
||
st1
->
finished
||
(
st1
->
head_ts
!=
AV_NOPTS_VALUE
&&
av_compare_ts
(
tail_ts
,
st
->
tb
,
st1
->
head_ts
,
st1
->
tb
)
<=
0
))
continue
;
ts
=
av_rescale_q
(
tail_ts
,
st
->
tb
,
st1
->
tb
);
if
(
st1
->
head_ts
!=
AV_NOPTS_VALUE
)
ts
=
FFMAX
(
st1
->
head_ts
+
1
,
ts
);
stream_update_ts
(
sq
,
i
,
ts
);
}
return
1
;
}
int
sq_send
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
,
SyncQueueFrame
frame
)
{
SyncQueueStream
*
st
;
SyncQueueFrame
dst
;
int64_t
ts
;
int
ret
;
av_assert0
(
stream_idx
<
sq
->
nb_streams
);
st
=
&
sq
->
streams
[
stream_idx
];
av_assert0
(
st
->
tb
.
num
>
0
&&
st
->
tb
.
den
>
0
);
if
(
frame_null
(
sq
,
frame
))
{
finish_stream
(
sq
,
stream_idx
);
return
0
;
}
if
(
st
->
finished
)
return
AVERROR_EOF
;
ret
=
objpool_get
(
sq
->
pool
,
(
void
**
)
&
dst
);
if
(
ret
<
0
)
return
ret
;
frame_move
(
sq
,
dst
,
frame
);
ts
=
frame_ts
(
sq
,
dst
);
ret
=
av_fifo_write
(
st
->
fifo
,
&
dst
,
1
);
if
(
ret
<
0
)
{
frame_move
(
sq
,
frame
,
dst
);
objpool_release
(
sq
->
pool
,
(
void
**
)
&
dst
);
return
ret
;
}
stream_update_ts
(
sq
,
stream_idx
,
ts
);
st
->
frames_sent
++
;
if
(
st
->
frames_sent
>=
st
->
frames_max
)
finish_stream
(
sq
,
stream_idx
);
return
0
;
}
static
int
receive_for_stream
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
,
SyncQueueFrame
frame
)
{
SyncQueueStream
*
st_head
=
sq
->
head_stream
>=
0
?
&
sq
->
streams
[
sq
->
head_stream
]
:
NULL
;
SyncQueueStream
*
st
;
av_assert0
(
stream_idx
<
sq
->
nb_streams
);
st
=
&
sq
->
streams
[
stream_idx
];
if
(
av_fifo_can_read
(
st
->
fifo
))
{
SyncQueueFrame
peek
;
int64_t
ts
;
int
cmp
=
1
;
av_fifo_peek
(
st
->
fifo
,
&
peek
,
1
,
0
);
ts
=
frame_ts
(
sq
,
peek
);
/* check if this stream's tail timestamp does not overtake
* the overall queue head */
if
(
ts
!=
AV_NOPTS_VALUE
&&
st_head
)
cmp
=
av_compare_ts
(
ts
,
st
->
tb
,
st_head
->
head_ts
,
st_head
->
tb
);
/* We can release frames that do not end after the queue head.
* Frames with no timestamps are just passed through with no conditions.
*/
if
(
cmp
<=
0
||
ts
==
AV_NOPTS_VALUE
)
{
frame_move
(
sq
,
frame
,
peek
);
objpool_release
(
sq
->
pool
,
(
void
**
)
&
peek
);
av_fifo_drain2
(
st
->
fifo
,
1
);
return
0
;
}
}
return
(
sq
->
finished
||
(
st
->
finished
&&
!
av_fifo_can_read
(
st
->
fifo
)))
?
AVERROR_EOF
:
AVERROR
(
EAGAIN
);
}
static
int
receive_internal
(
SyncQueue
*
sq
,
int
stream_idx
,
SyncQueueFrame
frame
)
{
int
nb_eof
=
0
;
int
ret
;
/* read a frame for a specific stream */
if
(
stream_idx
>=
0
)
{
ret
=
receive_for_stream
(
sq
,
stream_idx
,
frame
);
return
(
ret
<
0
)
?
ret
:
stream_idx
;
}
/* read a frame for any stream with available output */
for
(
unsigned
int
i
=
0
;
i
<
sq
->
nb_streams
;
i
++
)
{
ret
=
receive_for_stream
(
sq
,
i
,
frame
);
if
(
ret
==
AVERROR_EOF
||
ret
==
AVERROR
(
EAGAIN
))
{
nb_eof
+=
(
ret
==
AVERROR_EOF
);
continue
;
}
return
(
ret
<
0
)
?
ret
:
i
;
}
return
(
nb_eof
==
sq
->
nb_streams
)
?
AVERROR_EOF
:
AVERROR
(
EAGAIN
);
}
int
sq_receive
(
SyncQueue
*
sq
,
int
stream_idx
,
SyncQueueFrame
frame
)
{
int
ret
=
receive_internal
(
sq
,
stream_idx
,
frame
);
/* try again if the queue overflowed and triggered a fake heartbeat
* for lagging streams */
if
(
ret
==
AVERROR
(
EAGAIN
)
&&
overflow_heartbeat
(
sq
,
stream_idx
))
ret
=
receive_internal
(
sq
,
stream_idx
,
frame
);
return
ret
;
}
int
sq_add_stream
(
SyncQueue
*
sq
,
int
limiting
)
{
SyncQueueStream
*
tmp
,
*
st
;
tmp
=
av_realloc_array
(
sq
->
streams
,
sq
->
nb_streams
+
1
,
sizeof
(
*
sq
->
streams
));
if
(
!
tmp
)
return
AVERROR
(
ENOMEM
);
sq
->
streams
=
tmp
;
st
=
&
sq
->
streams
[
sq
->
nb_streams
];
memset
(
st
,
0
,
sizeof
(
*
st
));
st
->
fifo
=
av_fifo_alloc2
(
1
,
sizeof
(
SyncQueueFrame
),
AV_FIFO_FLAG_AUTO_GROW
);
if
(
!
st
->
fifo
)
return
AVERROR
(
ENOMEM
);
/* we set a valid default, so that a pathological stream that never
* receives even a real timebase (and no frames) won't stall all other
* streams forever; cf. overflow_heartbeat() */
st
->
tb
=
(
AVRational
){
1
,
1
};
st
->
head_ts
=
AV_NOPTS_VALUE
;
st
->
frames_max
=
UINT64_MAX
;
st
->
limiting
=
limiting
;
return
sq
->
nb_streams
++
;
}
void
sq_set_tb
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
,
AVRational
tb
)
{
SyncQueueStream
*
st
;
av_assert0
(
stream_idx
<
sq
->
nb_streams
);
st
=
&
sq
->
streams
[
stream_idx
];
av_assert0
(
!
av_fifo_can_read
(
st
->
fifo
));
if
(
st
->
head_ts
!=
AV_NOPTS_VALUE
)
st
->
head_ts
=
av_rescale_q
(
st
->
head_ts
,
st
->
tb
,
tb
);
st
->
tb
=
tb
;
}
void
sq_limit_frames
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
,
uint64_t
frames
)
{
SyncQueueStream
*
st
;
av_assert0
(
stream_idx
<
sq
->
nb_streams
);
st
=
&
sq
->
streams
[
stream_idx
];
st
->
frames_max
=
frames
;
if
(
st
->
frames_sent
>=
st
->
frames_max
)
finish_stream
(
sq
,
stream_idx
);
}
SyncQueue
*
sq_alloc
(
enum
SyncQueueType
type
,
int64_t
buf_size_us
)
{
SyncQueue
*
sq
=
av_mallocz
(
sizeof
(
*
sq
));
if
(
!
sq
)
return
NULL
;
sq
->
type
=
type
;
sq
->
buf_size_us
=
buf_size_us
;
sq
->
head_stream
=
-
1
;
sq
->
head_finished_stream
=
-
1
;
sq
->
pool
=
(
type
==
SYNC_QUEUE_PACKETS
)
?
objpool_alloc_packets
()
:
objpool_alloc_frames
();
if
(
!
sq
->
pool
)
{
av_freep
(
&
sq
);
return
NULL
;
}
return
sq
;
}
void
sq_free
(
SyncQueue
**
psq
)
{
SyncQueue
*
sq
=
*
psq
;
if
(
!
sq
)
return
;
for
(
unsigned
int
i
=
0
;
i
<
sq
->
nb_streams
;
i
++
)
{
SyncQueueFrame
frame
;
while
(
av_fifo_read
(
sq
->
streams
[
i
].
fifo
,
&
frame
,
1
)
>=
0
)
objpool_release
(
sq
->
pool
,
(
void
**
)
&
frame
);
av_fifo_freep2
(
&
sq
->
streams
[
i
].
fifo
);
}
av_freep
(
&
sq
->
streams
);
objpool_free
(
&
sq
->
pool
);
av_freep
(
psq
);
}
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.h
0 → 100644
浏览文件 @
f23762b9
/*
* This file is part of FFmpeg.
* Copyright (c) 2023 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of sync_queue.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
*/
#ifndef FFTOOLS_SYNC_QUEUE_H
#define FFTOOLS_SYNC_QUEUE_H
#include <stdint.h>
#include "libavcodec/packet.h"
#include "libavutil/frame.h"
enum
SyncQueueType
{
SYNC_QUEUE_PACKETS
,
SYNC_QUEUE_FRAMES
,
};
typedef
union
SyncQueueFrame
{
AVFrame
*
f
;
AVPacket
*
p
;
}
SyncQueueFrame
;
#define SQFRAME(frame) ((SyncQueueFrame){ .f = (frame) })
#define SQPKT(pkt) ((SyncQueueFrame){ .p = (pkt) })
typedef
struct
SyncQueue
SyncQueue
;
/**
* Allocate a sync queue of the given type.
*
* @param buf_size_us maximum duration that will be buffered in microseconds
*/
SyncQueue
*
sq_alloc
(
enum
SyncQueueType
type
,
int64_t
buf_size_us
);
void
sq_free
(
SyncQueue
**
sq
);
/**
* Add a new stream to the sync queue.
*
* @param limiting whether the stream is limiting, i.e. no other stream can be
* longer than this one
* @return
* - a non-negative stream index on success
* - a negative error code on error
*/
int
sq_add_stream
(
SyncQueue
*
sq
,
int
limiting
);
/**
* Set the timebase for the stream with index stream_idx. Should be called
* before sending any frames for this stream.
*/
void
sq_set_tb
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
,
AVRational
tb
);
/**
* Limit the number of output frames for stream with index stream_idx
* to max_frames.
*/
void
sq_limit_frames
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
,
uint64_t
max_frames
);
/**
* Submit a frame for the stream with index stream_idx.
*
* On success, the sync queue takes ownership of the frame and will reset the
* contents of the supplied frame. On failure, the frame remains owned by the
* caller.
*
* Sending a frame with NULL contents marks the stream as finished.
*
* @return
* - 0 on success
* - AVERROR_EOF when no more frames should be submitted for this stream
* - another a negative error code on failure
*/
int
sq_send
(
SyncQueue
*
sq
,
unsigned
int
stream_idx
,
SyncQueueFrame
frame
);
/**
* Read a frame from the queue.
*
* @param stream_idx index of the stream to read a frame for. May be -1, then
* try to read a frame from any stream that is ready for
* output.
* @param frame output frame will be written here on success. The frame is owned
* by the caller.
*
* @return
* - a non-negative index of the stream to which the returned frame belongs
* - AVERROR(EAGAIN) when more frames need to be submitted to the queue
* - AVERROR_EOF when no more frames will be available for this stream (for any
* stream if stream_idx is -1)
* - another negative error code on failure
*/
int
sq_receive
(
SyncQueue
*
sq
,
int
stream_idx
,
SyncQueueFrame
frame
);
#endif // FFTOOLS_SYNC_QUEUE_H
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.c
0 → 100644
浏览文件 @
f23762b9
/*
* This file is part of FFmpeg.
* Copyright (c) 2023 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of thread_queue.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools header names updated
*/
#include <stdint.h>
#include <string.h>
#include "libavutil/avassert.h"
#include "libavutil/error.h"
#include "libavutil/fifo.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "libavutil/thread.h"
#include "fftools_objpool.h"
#include "fftools_thread_queue.h"
enum
{
FINISHED_SEND
=
(
1
<<
0
),
FINISHED_RECV
=
(
1
<<
1
),
};
typedef
struct
FifoElem
{
void
*
obj
;
unsigned
int
stream_idx
;
}
FifoElem
;
struct
ThreadQueue
{
int
*
finished
;
unsigned
int
nb_streams
;
AVFifo
*
fifo
;
ObjPool
*
obj_pool
;
void
(
*
obj_move
)(
void
*
dst
,
void
*
src
);
pthread_mutex_t
lock
;
pthread_cond_t
cond
;
};
void
tq_free
(
ThreadQueue
**
ptq
)
{
ThreadQueue
*
tq
=
*
ptq
;
if
(
!
tq
)
return
;
if
(
tq
->
fifo
)
{
FifoElem
elem
;
while
(
av_fifo_read
(
tq
->
fifo
,
&
elem
,
1
)
>=
0
)
objpool_release
(
tq
->
obj_pool
,
&
elem
.
obj
);
}
av_fifo_freep2
(
&
tq
->
fifo
);
objpool_free
(
&
tq
->
obj_pool
);
av_freep
(
&
tq
->
finished
);
pthread_cond_destroy
(
&
tq
->
cond
);
pthread_mutex_destroy
(
&
tq
->
lock
);
av_freep
(
ptq
);
}
ThreadQueue
*
tq_alloc
(
unsigned
int
nb_streams
,
size_t
queue_size
,
ObjPool
*
obj_pool
,
void
(
*
obj_move
)(
void
*
dst
,
void
*
src
))
{
ThreadQueue
*
tq
;
int
ret
;
tq
=
av_mallocz
(
sizeof
(
*
tq
));
if
(
!
tq
)
return
NULL
;
ret
=
pthread_cond_init
(
&
tq
->
cond
,
NULL
);
if
(
ret
)
{
av_freep
(
&
tq
);
return
NULL
;
}
ret
=
pthread_mutex_init
(
&
tq
->
lock
,
NULL
);
if
(
ret
)
{
pthread_cond_destroy
(
&
tq
->
cond
);
av_freep
(
&
tq
);
return
NULL
;
}
tq
->
finished
=
av_calloc
(
nb_streams
,
sizeof
(
*
tq
->
finished
));
if
(
!
tq
->
finished
)
goto
fail
;
tq
->
nb_streams
=
nb_streams
;
tq
->
fifo
=
av_fifo_alloc2
(
queue_size
,
sizeof
(
FifoElem
),
0
);
if
(
!
tq
->
fifo
)
goto
fail
;
tq
->
obj_pool
=
obj_pool
;
tq
->
obj_move
=
obj_move
;
return
tq
;
fail:
tq_free
(
&
tq
);
return
NULL
;
}
int
tq_send
(
ThreadQueue
*
tq
,
unsigned
int
stream_idx
,
void
*
data
)
{
int
*
finished
;
int
ret
;
av_assert0
(
stream_idx
<
tq
->
nb_streams
);
finished
=
&
tq
->
finished
[
stream_idx
];
pthread_mutex_lock
(
&
tq
->
lock
);
if
(
*
finished
&
FINISHED_SEND
)
{
ret
=
AVERROR
(
EINVAL
);
goto
finish
;
}
while
(
!
(
*
finished
&
FINISHED_RECV
)
&&
!
av_fifo_can_write
(
tq
->
fifo
))
pthread_cond_wait
(
&
tq
->
cond
,
&
tq
->
lock
);
if
(
*
finished
&
FINISHED_RECV
)
{
ret
=
AVERROR_EOF
;
*
finished
|=
FINISHED_SEND
;
}
else
{
FifoElem
elem
=
{
.
stream_idx
=
stream_idx
};
ret
=
objpool_get
(
tq
->
obj_pool
,
&
elem
.
obj
);
if
(
ret
<
0
)
goto
finish
;
tq
->
obj_move
(
elem
.
obj
,
data
);
ret
=
av_fifo_write
(
tq
->
fifo
,
&
elem
,
1
);
av_assert0
(
ret
>=
0
);
pthread_cond_broadcast
(
&
tq
->
cond
);
}
finish:
pthread_mutex_unlock
(
&
tq
->
lock
);
return
ret
;
}
static
int
receive_locked
(
ThreadQueue
*
tq
,
int
*
stream_idx
,
void
*
data
)
{
FifoElem
elem
;
unsigned
int
nb_finished
=
0
;
if
(
av_fifo_read
(
tq
->
fifo
,
&
elem
,
1
)
>=
0
)
{
tq
->
obj_move
(
data
,
elem
.
obj
);
objpool_release
(
tq
->
obj_pool
,
&
elem
.
obj
);
*
stream_idx
=
elem
.
stream_idx
;
return
0
;
}
for
(
unsigned
int
i
=
0
;
i
<
tq
->
nb_streams
;
i
++
)
{
if
(
!
(
tq
->
finished
[
i
]
&
FINISHED_SEND
))
continue
;
/* return EOF to the consumer at most once for each stream */
if
(
!
(
tq
->
finished
[
i
]
&
FINISHED_RECV
))
{
tq
->
finished
[
i
]
|=
FINISHED_RECV
;
*
stream_idx
=
i
;
return
AVERROR_EOF
;
}
nb_finished
++
;
}
return
nb_finished
==
tq
->
nb_streams
?
AVERROR_EOF
:
AVERROR
(
EAGAIN
);
}
int
tq_receive
(
ThreadQueue
*
tq
,
int
*
stream_idx
,
void
*
data
)
{
int
ret
;
*
stream_idx
=
-
1
;
pthread_mutex_lock
(
&
tq
->
lock
);
while
(
1
)
{
ret
=
receive_locked
(
tq
,
stream_idx
,
data
);
if
(
ret
==
AVERROR
(
EAGAIN
))
{
pthread_cond_wait
(
&
tq
->
cond
,
&
tq
->
lock
);
continue
;
}
break
;
}
if
(
ret
==
0
)
pthread_cond_broadcast
(
&
tq
->
cond
);
pthread_mutex_unlock
(
&
tq
->
lock
);
return
ret
;
}
void
tq_send_finish
(
ThreadQueue
*
tq
,
unsigned
int
stream_idx
)
{
av_assert0
(
stream_idx
<
tq
->
nb_streams
);
pthread_mutex_lock
(
&
tq
->
lock
);
/* mark the stream as send-finished;
* next time the consumer thread tries to read this stream it will get
* an EOF and recv-finished flag will be set */
tq
->
finished
[
stream_idx
]
|=
FINISHED_SEND
;
pthread_cond_broadcast
(
&
tq
->
cond
);
pthread_mutex_unlock
(
&
tq
->
lock
);
}
void
tq_receive_finish
(
ThreadQueue
*
tq
,
unsigned
int
stream_idx
)
{
av_assert0
(
stream_idx
<
tq
->
nb_streams
);
pthread_mutex_lock
(
&
tq
->
lock
);
/* mark the stream as recv-finished;
* next time the producer thread tries to send for this stream, it will
* get an EOF and send-finished flag will be set */
tq
->
finished
[
stream_idx
]
|=
FINISHED_RECV
;
pthread_cond_broadcast
(
&
tq
->
cond
);
pthread_mutex_unlock
(
&
tq
->
lock
);
}
android/ffmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.h
0 → 100644
浏览文件 @
f23762b9
/*
* This file is part of FFmpeg.
* Copyright (c) 2023 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of thread_queue.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
*/
#ifndef FFTOOLS_THREAD_QUEUE_H
#define FFTOOLS_THREAD_QUEUE_H
#include <string.h>
#include "fftools_objpool.h"
typedef
struct
ThreadQueue
ThreadQueue
;
/**
* Allocate a queue for sending data between threads.
*
* @param nb_streams number of streams for which a distinct EOF state is
* maintained
* @param queue_size number of items that can be stored in the queue without
* blocking
* @param obj_pool object pool that will be used to allocate items stored in the
* queue; the pool becomes owned by the queue
* @param callback that moves the contents between two data pointers
*/
ThreadQueue
*
tq_alloc
(
unsigned
int
nb_streams
,
size_t
queue_size
,
ObjPool
*
obj_pool
,
void
(
*
obj_move
)(
void
*
dst
,
void
*
src
));
void
tq_free
(
ThreadQueue
**
tq
);
/**
* Send an item for the given stream to the queue.
*
* @param data the item to send, its contents will be moved using the callback
* provided to tq_alloc(); on failure the item will be left
* untouched
* @return
* - 0 the item was successfully sent
* - AVERROR(ENOMEM) could not allocate an item for writing to the FIFO
* - AVERROR(EINVAL) the sending side has previously been marked as finished
* - AVERROR_EOF the receiving side has marked the given stream as finished
*/
int
tq_send
(
ThreadQueue
*
tq
,
unsigned
int
stream_idx
,
void
*
data
);
/**
* Mark the given stream finished from the sending side.
*/
void
tq_send_finish
(
ThreadQueue
*
tq
,
unsigned
int
stream_idx
);
/**
* Read the next item from the queue.
*
* @param stream_idx the index of the stream that was processed or -1 will be
* written here
* @param data the data item will be written here on success using the
* callback provided to tq_alloc()
* @return
* - 0 a data item was successfully read; *stream_idx contains a non-negative
* stream index
* - AVERROR_EOF When *stream_idx is non-negative, this signals that the sending
* side has marked the given stream as finished. This will happen at most once
* for each stream. When *stream_idx is -1, all streams are done.
*/
int
tq_receive
(
ThreadQueue
*
tq
,
int
*
stream_idx
,
void
*
data
);
/**
* Mark the given stream finished from the receiving side.
*/
void
tq_receive_finish
(
ThreadQueue
*
tq
,
unsigned
int
stream_idx
);
#endif // FFTOOLS_THREAD_QUEUE_H
android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java
浏览文件 @
f23762b9
...
@@ -144,8 +144,6 @@ public class FFmpegKitConfig {
...
@@ -144,8 +144,6 @@ public class FFmpegKitConfig {
NativeLoader
.
loadFFmpegKit
(
nativeFFmpegTriedAndFailed
);
NativeLoader
.
loadFFmpegKit
(
nativeFFmpegTriedAndFailed
);
android
.
util
.
Log
.
i
(
FFmpegKitConfig
.
TAG
,
String
.
format
(
"Loaded ffmpeg-kit-%s-%s-%s-%s."
,
NativeLoader
.
loadPackageName
(),
NativeLoader
.
loadAbi
(),
NativeLoader
.
loadVersion
(),
NativeLoader
.
loadBuildDate
()));
uniqueIdGenerator
=
new
AtomicInteger
(
1
);
uniqueIdGenerator
=
new
AtomicInteger
(
1
);
/* NATIVE LOG LEVEL IS RECEIVED ONLY ON STARTUP */
/* NATIVE LOG LEVEL IS RECEIVED ONLY ON STARTUP */
...
@@ -175,7 +173,7 @@ public class FFmpegKitConfig {
...
@@ -175,7 +173,7 @@ public class FFmpegKitConfig {
safFileDescriptorMap
=
new
SparseArray
<>();
safFileDescriptorMap
=
new
SparseArray
<>();
globalLogRedirectionStrategy
=
LogRedirectionStrategy
.
PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED
;
globalLogRedirectionStrategy
=
LogRedirectionStrategy
.
PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED
;
NativeLoader
.
enableRedirection
(
);
android
.
util
.
Log
.
i
(
FFmpegKitConfig
.
TAG
,
String
.
format
(
"Loaded ffmpeg-kit-%s-%s-%s-%s."
,
NativeLoader
.
loadPackageName
(),
NativeLoader
.
loadAbi
(),
NativeLoader
.
loadVersion
(),
NativeLoader
.
loadBuildDate
())
);
}
}
/**
/**
...
@@ -336,7 +334,7 @@ public class FFmpegKitConfig {
...
@@ -336,7 +334,7 @@ public class FFmpegKitConfig {
*/
*/
private
static
void
statistics
(
final
long
sessionId
,
final
int
videoFrameNumber
,
private
static
void
statistics
(
final
long
sessionId
,
final
int
videoFrameNumber
,
final
float
videoFps
,
final
float
videoQuality
,
final
long
size
,
final
float
videoFps
,
final
float
videoQuality
,
final
long
size
,
final
int
time
,
final
double
bitrate
,
final
double
speed
)
{
final
double
time
,
final
double
bitrate
,
final
double
speed
)
{
final
Statistics
statistics
=
new
Statistics
(
sessionId
,
videoFrameNumber
,
videoFps
,
videoQuality
,
size
,
time
,
bitrate
,
speed
);
final
Statistics
statistics
=
new
Statistics
(
sessionId
,
videoFrameNumber
,
videoFps
,
videoQuality
,
size
,
time
,
bitrate
,
speed
);
final
Session
session
=
getSession
(
sessionId
);
final
Session
session
=
getSession
(
sessionId
);
...
...
android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/NativeLoader.java
浏览文件 @
f23762b9
...
@@ -36,7 +36,7 @@ public class NativeLoader {
...
@@ -36,7 +36,7 @@ public class NativeLoader {
static
final
String
[]
FFMPEG_LIBRARIES
=
{
"avutil"
,
"swscale"
,
"swresample"
,
"avcodec"
,
"avformat"
,
"avfilter"
,
"avdevice"
};
static
final
String
[]
FFMPEG_LIBRARIES
=
{
"avutil"
,
"swscale"
,
"swresample"
,
"avcodec"
,
"avformat"
,
"avfilter"
,
"avdevice"
};
static
final
String
[]
LIBRARIES_LINKED_WITH_CXX
=
{
"chromaprint"
,
"openh264"
,
"rubberband"
,
"snappy"
,
"srt"
,
"tesseract"
,
"x265"
,
"zimg"
};
static
final
String
[]
LIBRARIES_LINKED_WITH_CXX
=
{
"chromaprint"
,
"openh264"
,
"rubberband"
,
"snappy"
,
"srt"
,
"tesseract"
,
"x265"
,
"zimg"
,
"libilbc"
};
static
boolean
isTestModeDisabled
()
{
static
boolean
isTestModeDisabled
()
{
return
(
System
.
getProperty
(
"enable.ffmpeg.kit.test.mode"
)
==
null
);
return
(
System
.
getProperty
(
"enable.ffmpeg.kit.test.mode"
)
==
null
);
...
@@ -85,7 +85,7 @@ public class NativeLoader {
...
@@ -85,7 +85,7 @@ public class NativeLoader {
}
}
static
String
loadVersion
()
{
static
String
loadVersion
()
{
final
String
version
=
"
5.1
"
;
final
String
version
=
"
6.0
"
;
if
(
isTestModeDisabled
())
{
if
(
isTestModeDisabled
())
{
return
FFmpegKitConfig
.
getVersion
();
return
FFmpegKitConfig
.
getVersion
();
...
...
android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/Statistics.java
浏览文件 @
f23762b9
...
@@ -28,11 +28,11 @@ public class Statistics {
...
@@ -28,11 +28,11 @@ public class Statistics {
private
float
videoFps
;
private
float
videoFps
;
private
float
videoQuality
;
private
float
videoQuality
;
private
long
size
;
private
long
size
;
private
int
time
;
private
double
time
;
private
double
bitrate
;
private
double
bitrate
;
private
double
speed
;
private
double
speed
;
public
Statistics
(
final
long
sessionId
,
final
int
videoFrameNumber
,
final
float
videoFps
,
final
float
videoQuality
,
final
long
size
,
final
int
time
,
final
double
bitrate
,
final
double
speed
)
{
public
Statistics
(
final
long
sessionId
,
final
int
videoFrameNumber
,
final
float
videoFps
,
final
float
videoQuality
,
final
long
size
,
final
double
time
,
final
double
bitrate
,
final
double
speed
)
{
this
.
sessionId
=
sessionId
;
this
.
sessionId
=
sessionId
;
this
.
videoFrameNumber
=
videoFrameNumber
;
this
.
videoFrameNumber
=
videoFrameNumber
;
this
.
videoFps
=
videoFps
;
this
.
videoFps
=
videoFps
;
...
@@ -83,11 +83,11 @@ public class Statistics {
...
@@ -83,11 +83,11 @@ public class Statistics {
this
.
size
=
size
;
this
.
size
=
size
;
}
}
public
int
getTime
()
{
public
double
getTime
()
{
return
time
;
return
time
;
}
}
public
void
setTime
(
int
time
)
{
public
void
setTime
(
double
time
)
{
this
.
time
=
time
;
this
.
time
=
time
;
}
}
...
...
android/jni/Android.mk
浏览文件 @
f23762b9
...
@@ -69,7 +69,7 @@ include $(BUILD_SHARED_LIBRARY)
...
@@ -69,7 +69,7 @@ include $(BUILD_SHARED_LIBRARY)
$(call import-module, cpu-features)
$(call import-module, cpu-features)
MY_SRC_FILES := ffmpegkit.c ffprobekit.c ffmpegkit_exception.c fftools_cmdutils.c fftools_ffmpeg.c fftools_ffprobe.c fftools_ffmpeg_mux.c fftools_ffmpeg_
opt.c fftools_opt_common.c fftools_ffmpeg_hw.c fftools_ffmpeg_filter
.c
MY_SRC_FILES := ffmpegkit.c ffprobekit.c ffmpegkit_exception.c fftools_cmdutils.c fftools_ffmpeg.c fftools_ffprobe.c fftools_ffmpeg_mux.c fftools_ffmpeg_
mux_init.c fftools_ffmpeg_demux.c fftools_ffmpeg_opt.c fftools_opt_common.c fftools_ffmpeg_hw.c fftools_ffmpeg_filter.c fftools_objpool.c fftools_sync_queue.c fftools_thread_queue
.c
ifeq ($(TARGET_PLATFORM),android-16)
ifeq ($(TARGET_PLATFORM),android-16)
MY_SRC_FILES += android_lts_support.c
MY_SRC_FILES += android_lts_support.c
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论