#android #video #android-ndk #media #frames
Вопрос:
Я пытаюсь извлечь кадры из медиафайла с помощью NDK. Код, написанный на уровне C . Я прилагаю написанный мной код.
Эта функция находится в NDK, используемой для хранения кадров в каталоге SD-карт
void WriteFile(AImage* image) {
int planeCount;
media_status_t status = AImage_getNumberOfPlanes(image, amp;planeCount);
ASSERT(status == AMEDIA_OK amp;amp; planeCount == 1,
"Error: getNumberOfPlanes() planeCount = %d", planeCount);
uint8_t *data = nullptr;
int len = 0;
AImage_getPlaneData(image, 0, amp;data, amp;len);
DIR *dir = opendir(kDirName);
if (dir) {
closedir(dir);
} else {
std::string cmd = "mkdir -p ";
cmd = kDirName;
system(cmd.c_str());
}
struct timespec ts {
0, 0
};
clock_gettime(CLOCK_REALTIME, amp;ts);
struct tm localTime;
localtime_r(amp;ts.tv_sec, amp;localTime);
std::string fileName = kDirName;
std::string dash("-");
fileName = kFileName std::to_string(localTime.tm_mon)
std::to_string(localTime.tm_mday) dash
std::to_string(localTime.tm_hour)
std::to_string(localTime.tm_min)
std::to_string(localTime.tm_sec) ".jpg";
FILE *file = fopen(fileName.c_str(), "wb");
if (file amp;amp; data amp;amp; len) {
fwrite(data, 1, len, file);
fclose(file);
if (callback_) {
callback_(callbackCtx_, fileName.c_str());
}
} else {
if (file)
fclose(file);
}
AImage_delete(image);
}
void imageCallback(void *context, AImageReader *reader) {
int32_t format;
media_status_t status = AImageReader_getFormat (reader, amp;format);
AImage* image;
status = AImageReader_acquireLatestImage(reader, amp;image);
status = AImage_getFormat(image, amp;format);
// Create a thread and write out the jpeg files
std::thread writeFileHandler(amp;ImageReader::WriteFile, this, image);
writeFileHandler.detach();
}
This function resides in C layer which is called from Java layer
extern "C" {
jboolean Java_com_example_nativecodec_NativeCodec_createSampleMediaPlayer(JNIEnv* env,
jclass clazz, jobject assetMgr, jstring filename)
{
LOGV("@@@ create");
// convert Java string to UTF-8
const char *utf8 = env->GetStringUTFChars(filename, NULL);
LOGV("opening %s", utf8);
off_t outStart, outLen;
int fd = AAsset_openFileDescriptor(AAssetManager_open(AAssetManager_fromJava(env, assetMgr),
utf8, 0), amp;outStart, amp;outLen);
env->ReleaseStringUTFChars(filename, utf8);
if (fd < 0) {
LOGE("failed to open file: %s %d (%s)", utf8, fd, strerror(errno));
return JNI_FALSE;
}
data.fd = fd;
workerdata *d = amp;data;
AMediaExtractor *ex = AMediaExtractor_new();
media_status_t err = AMediaExtractor_setDataSourceFd(ex, d->fd, static_cast<off64_t>
(outStart), static_cast<off64_t>(outLen));
close(d->fd);
if (err != AMEDIA_OK) {
LOGV("setDataSource error: %d", err);
return JNI_FALSE;
}
int numtracks = AMediaExtractor_getTrackCount(ex);
AMediaCodec *codec = NULL;
LOGV("input has %d tracks", numtracks);
for (int i = 0; i < numtracks; i ) {
AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
const char *s = AMediaFormat_toString(format);
LOGV("track %d format: %s", i, s);
const char *mime;
if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, amp;mime)) {
LOGV("no mime type");
return JNI_FALSE;
} else if (!strncmp(mime, "video/", 6)) {
// Omitting most error handling for clarity.
// Production code should check for errors.
// Configure the decoder to render to a surface
AMediaExtractor_selectTrack(ex, i);
codec = AMediaCodec_createDecoderByType(mime);
AImageReader* imageReader;
ANativeWindow* surface;
// This setting works
media_status_t status = AImageReader_new(480, 360, AIMAGE_FORMAT_YUV_420_888, 1,
amp;imageReader);
status = AImageReader_getWindow(imageReader, amp;surface);
AImageReader_ImageListener* imageListener = new AImageReader_ImageListener();
imageListener->onImageAvailable = amp;imageCallback;
AImageReader_setImageListener(imageReader, imageListener);
AMediaCodec_configure(codec, format, d->window, NULL, 0);
d->ex = ex;
d->codec = codec;
d->renderstart = -1;
d->sawInputEOS = false;
d->sawOutputEOS = false;
d->isPlaying = false;
d->renderonce = true;
AMediaCodec_start(codec);
}
AMediaFormat_delete(format);
}
mlooper = new mylooper();
mlooper->post(kMsgCodecBuffer, d);
return JNI_TRUE;
}
Это имя каталога и имена файлов SD-карты.
static const char *kDirName = "/sdcard/DCIM/Camera/";
static const char *kFileName = "capture";
Объявление структуры в native-codec-jni.cpp
typedef struct {
int fd;
ANativeWindow* window;
AMediaExtractor* ex;
AMediaCodec *codec;
int64_t renderstart;
bool sawInputEOS;
bool sawOutputEOS;
bool isPlaying;
bool renderonce;
} workerdata;
workerdata data = {-1, NULL, NULL, NULL, 0, false, false, false, false};
void *callbackCtx_;
std::function<void(void *ctx, const char* fileName)> callback_;
void RegisterCallback(void* ctx, std::function<void(void* ctx, const char*fileName)> func) {
callbackCtx_ = ctx;
callback_ = func;
}
Это фактический код вызова. Я сохранил видеоклип в папке «Ресурсы» и отправил по этому пути.
if(mRadio3.isChecked()){
mSampleCreated = createSampleMediaPlayer(getResources().getAssets(),mSourceString);
}
Когда я выполнил код, ошибок не было, но я не вижу никаких изображений на SD-карте.
Ищу помощи.