FFmpeg: поиск невозможен с дескриптором файла на Android Q
Учитывая тот факт, что общедоступные пути к файлам обычно не будут доступны в Android Q, я пытаюсь выяснить, как заставить мой аудио декодер FFmpeg работать с файловыми дескрипторами, не копируя файл в частные каталоги моего приложения.
Мы можем легко получить файловый дескриптор, используя методы, описанные в изменениях конфиденциальности Android Q, и можно открыть файловый дескриптор, используя конвейерный протокол, как описано в разделе Передача собственного fd int в FFMPEG из открываемого URI. Тем не менее, результат не доступен для поиска с помощью av_seek_frame
а также продолжительность не доступна с использованием члена длительности AVFormatContext
,
Есть ли способ поиска с файловым дескриптором с FFmpeg и получить продолжительность?
0 ответов
можно открыть дескриптор файла, используя протокол канала, как описано
Мне любопытно, зачем нужно открывать файловый дескриптор по конвейерному протоколу? Плеер sView открывает файловый дескриптор с помощью пользовательского AVIOContext, который доступен для поиска, по крайней мере, в более старых протестированных версиях Android. Вот псевдокод, открывающий AVFormatContext с настраиваемым AVIOContext.
int aFileDescriptor = myResMgr->openFileDescriptor(theFileToLoad);
AVFormatContext* aFormatCtx = avformat_alloc_context();
StAVIOContext myAvioContext;
if(!myAvioContext.openFromDescriptor(aFileDescriptor, "rb")) {
// error
}
aFormatCtx->pb = myAvioContext.getAvioContext();
int avErrCode = avformat_open_input(&aFormatCtx, theFileToLoad, NULL, NULL);
Ниже представлена попытка извлечь упрощенное определение класса StAVIOFileContext.
//! Wrapper over AVIOContext for passing the custom I/O.
class StAVIOContext {
public:
//! Main constructor.
StAVIOContext() {
const int aBufferSize = 32768;
unsigned char* aBufferIO = (unsigned char* )av_malloc(aBufferSize + AV_INPUT_BUFFER_PADDING_SIZE);
AVIOContext* myAvioCtx = avio_alloc_context (aBufferIO, aBufferSize, 0, this, readCallback, writeCallback, seekCallback);
}
//! Destructor.
virtual ~StAVIOContext() {
close();
if (myAvioCtx != NULL) { av_free (myAvioCtx); }
}
//! Close the file.
void close() {
if(myFile != NULL) {
fclose(myFile);
myFile = NULL;
}
}
//! Associate a stream with a file that was previously opened for low-level I/O.
//! The associated file will be automatically closed on destruction.
bool openFromDescriptor(int theFD, const char* theMode) {
close();
#ifdef _WIN32
myFile = ::_fdopen(theFD, theMode);
#else
myFile = ::fdopen(theFD, theMode);
#endif
return myFile != NULL;
}
//! Access AVIO context.
AVIOContext* getAvioContext() const { return myAvioCtx; }
public:
//! Virtual method for reading the data.
virtual int read (uint8_t* theBuf,
int theBufSize) {
if(myFile == NULL) { return -1; }
int aNbRead = (int )::fread(theBuf, 1, theBufSize, myFile);
if(aNbRead == 0 && feof(myFile) != 0) { return AVERROR_EOF; }
return aNbRead;
}
//! Virtual method for writing the data.
virtual int write (uint8_t* theBuf,
int theBufSize) {
if(myFile == NULL) { return -1; }
return (int )::fwrite(theBuf, 1, theBufSize, myFile);
}
//! Virtual method for seeking to new position.
virtual int64_t seek (int64_t theOffset,
int theWhence) {
if(theWhence == AVSEEK_SIZE || myFile == NULL) { return -1; }
#ifdef _WIN32
bool isOk = ::_fseeki64(myFile, theOffset, theWhence) == 0;
#else
bool isOk = ::fseeko(myFile, theOffset, theWhence) == 0;
#endif
if(!isOk) { return -1; }
#ifdef _WIN32
return ::_ftelli64(myFile);
#else
return ::ftello(myFile);
#endif
}
private:
//! Callback for reading the data.
static int readCallback(void* theOpaque,
uint8_t* theBuf,
int theBufSize) {
return theOpaque != NULL
? ((StAVIOContext* )theOpaque)->read(theBuf, theBufSize)
: 0;
}
//! Callback for writing the data.
static int writeCallback(void* theOpaque,
uint8_t* theBuf,
int theBufSize) {
return theOpaque != NULL
? ((StAVIOContext* )theOpaque)->write(theBuf, theBufSize)
: 0;
}
//! Callback for seeking to new position.
static int64_t seekCallback(void* theOpaque,
int64_t theOffset,
int theWhence) {
return theOpaque != NULL
? ((StAVIOContext* )theOpaque)->seek(theOffset, theWhence)
: -1;
}
protected:
AVIOContext* myAvioCtx;
FILE* myFile;
};
Пользовательский протокол может работать как Uricontent://com.android.providers.downloads.documents/document/msf%3A62
или content://com.android.externalstorage.documents/document/primary%3ADownload%2Ftranscode.aac
Вот код C, который открывает такой Uri (проверки ошибок скрыты для краткости):
int get_fd_from_content(const char *content, int access) {
static jclass android_net_Uri;
static jmethodID android_net_Uri_parse = 0;
static jmethodID android_content_Context_getContentResolver = 0;
static jmethodID android_content_ContentResolver_openFileDescriptor = 0;
static jmethodID android_os_ParcelFileDescriptor_getFd = 0;
int fd = -1;
JNIEnv *env;
int ret = (*globalVm)->GetEnv(globalVm, (void **)&env, JNI_VERSION_1_6);
android_net_Uri_parse = get_static_method_id(env, "android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", &android_net_Uri);
android_content_Context_getContentResolver = get_method_id(env, "android/content/Context", "getContentResolver", "()Landroid/content/ContentResolver;");
android_content_ContentResolver_openFileDescriptor = get_method_id(env, "android/content/ContentResolver", "openFileDescriptor", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;");
android_os_ParcelFileDescriptor_getFd = get_method_id(env, "android/os/ParcelFileDescriptor", "getFd", "()I"));
const char *fmode = "r";
if (access & (O_WRONLY | O_RDWR)) {
fmode = "w";
}
LOGI("get_fd_from_content" " \"%s\" fd from %s", fmode, content);
jstring uriString = (*env)->NewStringUTF(env, content);
jstring fmodeString = (*env)->NewStringUTF(env, fmode);
jobject uri = (*env)->CallStaticObjectMethod(env, android_net_Uri, android_net_Uri_parse, uriString);
jobject contentResolver = (*env)->CallObjectMethod(env, appContext, android_content_Context_getContentResolver);
jobject parcelFileDescriptor = (*env)->CallObjectMethod(env, contentResolver, android_content_ContentResolver_openFileDescriptor, uri, fmodeString);
fd = (*env)->CallIntMethod(env, parcelFileDescriptor, android_os_ParcelFileDescriptor_getFd);
(*env)->DeleteLocalRef(env, uriString);
(*env)->DeleteLocalRef(env, fmodeString);
(*env)->DeleteLocalRef(env, uri);
(*env)->DeleteLocalRef(env, contentResolver);
(*env)->DeleteLocalRef(env, parcelFileDescriptor);
return fd;
}