我使用自定义AVIOContext将FFMpeg与java IO联系起来。函数avformat_open_input()
永远不会返回。我在网上搜索了类似的问题,所有这些都是由网络故障或服务器配置错误引起的。但是,我根本没有使用网络,你可以在下面的小程序中看到:
package com.example;
import org.bytedeco.javacpp.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import static org.bytedeco.javacpp.avcodec.*;
import static org.bytedeco.javacpp.avformat.*;
import static org.bytedeco.javacpp.avutil.*;
import static org.bytedeco.javacpp.avdevice.*;
import static org.bytedeco.javacpp.avformat.AVFormatContext.*;
public class Test {
public static void main(String[] args) throws Exception {
File dir = new File(System.getProperty("user.home"), "Desktop");
File file = new File(dir, "sample.3gp");
final RandomAccessFile raf = new RandomAccessFile(file, "r");
Loader.load(avcodec.class);
Loader.load(avformat.class);
Loader.load(avutil.class);
Loader.load(avdevice.class);
Loader.load(swscale.class);
Loader.load(swresample.class);
avcodec_register_all();
av_register_all();
avformat_network_init();
avdevice_register_all();
Read_packet_Pointer_BytePointer_int reader = new Read_packet_Pointer_BytePointer_int() {
@Override
public int call(Pointer pointer, BytePointer buf, int bufSize) {
try {
byte[] data = new byte[bufSize]; // this is inefficient, just use as a quick example
int read = raf.read(data);
if (read <= 0) {
System.out.println("EOF found.");
return AVERROR_EOF;
}
System.out.println("Successfully read " + read + " bytes of data.");
buf.position(0);
buf.put(data, 0, read);
return read;
} catch (Exception ex) {
ex.printStackTrace();
return -1;
}
}
};
Seek_Pointer_long_int seeker = new Seek_Pointer_long_int() {
@Override
public long call(Pointer pointer, long offset, int whence) {
try {
raf.seek(offset);
System.out.println("Successfully seeked to position " + offset + ".");
return offset;
} catch (IOException ex) {
return -1;
}
}
};
int inputBufferSize = 32768;
BytePointer inputBuffer = new BytePointer(av_malloc(inputBufferSize));
AVIOContext ioContext = avio_alloc_context(inputBuffer, inputBufferSize, 1, null, reader, null, seeker);
AVInputFormat format = av_find_input_format("3gp");
AVFormatContext formatContext = avformat_alloc_context();
formatContext.iformat(format);
formatContext.flags(formatContext.flags() | AVFMT_FLAG_CUSTOM_IO);
formatContext.pb(ioContext);
// This never returns. And I can never get result.
int result = avformat_open_input(formatContext, "", format, null);
// all clean-up code omitted for simplicity
}
}
以下是我的示例控制台输出:
Successfully read 32768 bytes of data.
Successfully read 32768 bytes of data.
Successfully read 32768 bytes of data.
Successfully read 32768 bytes of data.
Successfully read 32768 bytes of data.
Successfully read 7240 bytes of data.
EOF found.
我检查了字节总和,它对应于文件大小; EOF也被击中,意味着文件被完全读取。实际上我有点怀疑为什么avformat_open_input()
甚至会读取整个文件而仍然没有返回?我正在做的事情肯定有问题。任何专家都可以点亮或指出我正确的方向吗?我是javacv
和ffmpeg
的新手,尤其是使用Buffer
和其他内容进行编程。欢迎任何帮助,建议或批评。提前谢谢。
答案 0 :(得分:4)
好的,现在我发现了问题。我有误解文档和忽略我发现的大多数示例。我的坏。
根据ffmpeg的文件:
AVIOContext* avio_alloc_context (unsigned char* buffer,
int buffer_size,
int write_flag,
void* opaque,
int(*)(void *opaque, uint8_t *buf, int buf_size) read_packet,
int(*)(void *opaque, uint8_t *buf, int buf_size) write_packet,
int64_t(*)(void *opaque, int64_t offset, int whence) seek
)
第三个参数write_flag
以下列方式使用:
write_flag - 如果缓冲区应该是可写的,则设置为1
,否则设置为0
。
实际上,这意味着如果AVIOContext
用于数据输出(即写作),write_flag
应设置为1
。否则,如果上下文用于数据输入(即读取),则应将其设置为0
。
在我发布的问题中,我将1
作为write_flag
传递,并且在阅读时导致问题。传递0
代替了解决问题。
稍后我重新阅读了我找到的所有示例,所有avio_alloc_context()
次调用都使用0
,而不是1
。这进一步说明了我遇到问题的原因。
总而言之,我将发布修订后的代码,并将纠正的问题作为未来参考。
package com.example;
import org.bytedeco.javacpp.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import static org.bytedeco.javacpp.avformat.*;
import static org.bytedeco.javacpp.avutil.*;
import static org.bytedeco.javacpp.avformat.AVFormatContext.*;
public class Test {
public static void main(String[] args) throws Exception {
File dir = new File(System.getProperty("user.home"), "Desktop");
File file = new File(dir, "sample.3gp");
final RandomAccessFile raf = new RandomAccessFile(file, "r");
Loader.load(avformat.class);
Loader.load(avutil.class);
av_register_all();
avformat_network_init();
Read_packet_Pointer_BytePointer_int reader = new Read_packet_Pointer_BytePointer_int() {
@Override
public int call(Pointer pointer, BytePointer buf, int bufSize) {
try {
byte[] data = new byte[bufSize]; // this is inefficient, just use as a quick example
int read = raf.read(data);
if (read <= 0) {
// I am still unsure as to return '0', '-1' or 'AVERROR_EOF'.
// But according to the following link, it should return 'AVERROR_EOF',
// http://www.codeproject.com/Tips/489450/Creating-Custom-FFmpeg-IO-Context
// btw 'AVERROR_EOF' is a nasty negative number, '-541478725'.
return AVERROR_EOF;
}
buf.position(0);
buf.put(data, 0, read);
return read;
} catch (Exception ex) {
ex.printStackTrace();
return -1;
}
}
};
Seek_Pointer_long_int seeker = new Seek_Pointer_long_int() {
@Override
public long call(Pointer pointer, long offset, int whence) {
try {
if (whence == AVSEEK_SIZE) {
// Returns the entire file length. If not supported, simply returns a negative number.
// https://www.ffmpeg.org/doxygen/trunk/avio_8h.html#a427ff2a881637b47ee7d7f9e368be63f
return raf.length();
}
raf.seek(offset);
return offset;
} catch (IOException ex) {
ex.printStackTrace();
return -1;
}
}
};
int inputBufferSize = 32768;
BytePointer inputBuffer = new BytePointer(av_malloc(inputBufferSize));
AVIOContext ioContext = avio_alloc_context(inputBuffer,
inputBufferSize,
0, // CRITICAL, if the context is for reading, it should be ZERO
// if the context is for writing, then it is ONE
null,
reader,
null,
seeker);
AVInputFormat format = av_find_input_format("3gp");
AVFormatContext formatContext = avformat_alloc_context();
formatContext.iformat(format);
formatContext.flags(formatContext.flags() | AVFMT_FLAG_CUSTOM_IO);
formatContext.pb(ioContext);
// Now this is working properly.
int result = avformat_open_input(formatContext, "", format, null);
System.out.println("result == " + result);
// all clean-up code omitted for simplicity
}
}
参考文献:
AVSEEK_SIZE
documentation avio_alloc_context()
documentation 其他参考资料:(我没有足够的声望点来获取更多链接,但我发现这些示例对我有帮助,所以我无论如何都用纯文本粘贴它们)
在以下位置创建自定义FFmpeg IO-Context(CodeProject示例):
http://www.codeproject.com/Tips/489450/Creating-Custom-FFmpeg-IO-Context
另一个示例显示在write_flag
中使用avio_alloc_context()
:
https://www.ffmpeg.org/doxygen/2.5/avio_reading_8c-example.html#a20
答案 1 :(得分:1)
您的搜索代码需要处理AVSEEK_SIZE,并且您的读取应该在EOF上返回0(“在读取文件结尾时,返回零。” - 来自man 2的字面引用读取),而不是AVERROR_EOF。