Compress Videos using FFMPEG and JNI - android

I want to create an android application which can locate a video file (which is more than 300 mb) and compress it to lower size mp4 file.
i already tried to do it with this
This tutorial is a very effective since you 're compressing a small size video (below than 100 mb)
So i tried to implement it using JNI .
i managed to build ffmpeg using this
But currently what I want to do is to compress videos . I don't have very good knowledge on JNI. But i tried to understand it using following link
If some one can guide me the steps to compress video after open file it using JNI that whould really great , thanks

Assuming you've got the String path of the input file, we can accomplish your task fairly easily. I'll assume you have an understanding of the NDK basics: How to connect a native .c file to native methods in a corresponding .java file (Let me know if that's part of your question). Instead I'll focus on how to use FFmpeg within the context of Android / JNI.
High-Level Overview:
#include <jni.h>
#include <android/log.h>
#include <string.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#define LOG_TAG "FFmpegWrapper"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
void Java_com_example_yourapp_yourJavaClass_compressFile(JNIEnv *env, jobject obj, jstring jInputPath, jstring jInputFormat, jstring jOutputPath, jstring JOutputFormat){
// One-time FFmpeg initialization
av_register_all();
avformat_network_init();
avcodec_register_all();
const char* inputPath = (*env)->GetStringUTFChars(env, jInputPath, NULL);
const char* outputPath = (*env)->GetStringUTFChars(env, jOutputPath, NULL);
// format names are hints. See available options on your host machine via $ ffmpeg -formats
const char* inputFormat = (*env)->GetStringUTFChars(env, jInputFormat, NULL);
const char* outputFormat = (*env)->GetStringUTFChars(env, jOutputFormat, NULL);
AVFormatContext *outputFormatContext = avFormatContextForOutputPath(outputPath, outputFormat);
AVFormatContext *inputFormatContext = avFormatContextForInputPath(inputPath, inputFormat /* not necessary since file can be inspected */);
copyAVFormatContext(&outputFormatContext, &inputFormatContext);
// Modify outputFormatContext->codec parameters per your liking
// See http://ffmpeg.org/doxygen/trunk/structAVCodecContext.html
int result = openFileForWriting(outputFormatContext, outputPath);
if(result < 0){
LOGE("openFileForWriting error: %d", result);
}
writeFileHeader(outputFormatContext);
// Copy input to output frame by frame
AVPacket *inputPacket;
inputPacket = av_malloc(sizeof(AVPacket));
int continueRecording = 1;
int avReadResult = 0;
int writeFrameResult = 0;
int frameCount = 0;
while(continueRecording == 1){
avReadResult = av_read_frame(inputFormatContext, inputPacket);
frameCount++;
if(avReadResult != 0){
if (avReadResult != AVERROR_EOF) {
LOGE("av_read_frame error: %s", stringForAVErrorNumber(avReadResult));
}else{
LOGI("End of input file");
}
continueRecording = 0;
}
AVStream *outStream = outputFormatContext->streams[inputPacket->stream_index];
writeFrameResult = av_interleaved_write_frame(outputFormatContext, inputPacket);
if(writeFrameResult < 0){
LOGE("av_interleaved_write_frame error: %s", stringForAVErrorNumber(avReadResult));
}
}
// Finalize the output file
int writeTrailerResult = writeFileTrailer(outputFormatContext);
if(writeTrailerResult < 0){
LOGE("av_write_trailer error: %s", stringForAVErrorNumber(writeTrailerResult));
}
LOGI("Wrote trailer");
}
For the full content of all the auxillary functions (the ones in camelCase), see my full project on Github. Got questions? I'm happy to elaborate.

Related

Directly call function in another .so file from C++ code in Android NDK

I have a.so which defines void a() and b.so which defines void b(). They are both put in the .apk so they are available to the Android application.
Now suppose that I'm calling a() through JNI. Is it possible to call b() from a() while completely bypassing JNI?
Can I do it this way in Android (the code is only for illustration, so it might have some errors)?
void a() {
void *handle = dlopen("b.so", RTLD_LAZY);
void (*b)() = dlsym(handle, "b");
b();
}
Would I need to add the fully qualified path, or is b.so already in LD_LIBRARY_PATH for the app?
You can do it this way on Android, though take care of where the shared library has been put in Android folders. It can change from a version to another.
On api 17 for example, it remains in /data/app-lib/. You can hardwrite it, but the best is to make calls to Java (through JNI) to know where the libraries should be.
We're doing something like this in our project :
JNIEnv* env;
const char* temp;
jobject oActivity = state->activity->clazz;
jclass cActivity = env->GetObjectClass(oActivity);
// get the path to where android extracts native libraries to
jmethodID midActivityGetApplicationInfo = env->GetMethodID(cActivity, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
jobject oApplicationInfo = env->CallObjectMethod(oActivity, midActivityGetApplicationInfo);
jclass cApplicationInfo = env->GetObjectClass(oApplicationInfo);
jfieldID fidApplicationInfoNativeLibraryDir = env->GetFieldID(cApplicationInfo, "nativeLibraryDir", "Ljava/lang/String;");
jstring sNativeLibraryDir = (jstring)env->GetObjectField(oApplicationInfo, fidApplicationInfoNativeLibraryDir);
temp = env->GetStringUTFChars(sNativeLibraryDir, NULL);
strcpy(libpath, temp);
strcat(libpath, "/");
Then you push your dlopen + dlsym combo in the fight and it should work.
As mentioned here : How do I load a shared object in C++?
There are two ways of loading shared objects in C++
For either of these methods you would always need the header file for the object you want to use. The header will contain the definitions of the classes or objects you want to use in your code.
#include "blah.h"
int main()
{
ClassFromBlah a;
a.DoSomething();
}
gcc yourfile.cpp -lblah
Dynamically (In Linux):
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen ("libm.so", RTLD_LAZY);
if (!handle) {
fprintf (stderr, "%s\n", dlerror());
exit(1);
}
dlerror(); /* Clear any existing error */
cosine = dlsym(handle, "cos");
if ((error = dlerror()) != NULL) {
fprintf (stderr, "%s\n", error);
exit(1);
}
printf ("%f\n", (*cosine)(2.0));
dlclose(handle);
return 0;
}
PS : for the dynamic approach, it depends on platform : on Linux, you use dlopen, on windows, you use LoadLibrary.

Opencv4Android: How to use with C++

I am trying too build a samples which using opencv for android. Here is my C++ code:
Header file:
#ifdef __cplusplus
extern "C" {
#endif
#include "jni.h"
#include "opencv2/core/core.hpp"
namespace openCVFuncs
{
cv::Mat contrastFilter(cv::Mat inputMatm, float contrastValue);
}
#ifdef __cplusplus
}
#endif
Cpp file:
namespace openCVFuncs
{
cv::Mat contrastFilter(cv::Mat inputMat, float contrastValue)
{
contrastValue = pow(2,contrastValue);
cv::Mat outMat = inputMat.clone();
uchar* data_img_in=(uchar*)inputMat.data;
uchar* data_img_out=(uchar*)outMat.data;
int temp = 0;
for(int i=0;i<inputMat.size().height;i++)
for(int j=0;j<inputMat.size().width;j++)
for (int c=0;c<inputMat.channels();c++)
{
temp = (data_img_in+inputMat.step[0]*i)[j*inputMat.channels()+c];
temp = (int)((temp - 128.0) * contrastValue) +128;
if (temp < 5) temp = 5;
if (temp > 255) temp = 255;
(data_img_out+outMat.step[0]*i)[j*outMat.channels()+c] = temp;
}
return outMat;
};
}
And I got many errors like that :
/opt/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/include/bits/valarray_before.h:652:3: error: template with C linkage
What wrong with my code ?
When using an "extern C"-block, you can only use the things that are available to C, so that rules out function overloading/polymorphism, namespaces, amongst other things.
In the header file you posted you include a .hpp file (which could possibly include one of the unusable definitions) and define a namespace.
This page gives some nice pointers on the subject on what you can and cannot do and how you can wrap calls to C++ namespaces / overloaded functions for use in a library compiled by a C compiler, see "Accessing C++ Code from Within C Source":
http://www.oracle.com/technetwork/articles/servers-storage-dev/mixingcandcpluspluscode-305840.html

use ffmpeg api to convert audio files. crash on avcodec_encode_audio2

From the examples I got the basic idea of this code.
However I am not sure, what I am missing, as muxing.c demuxing.c and decoding_encoding.c
all use different approaches.
The process of converting an audio file to another file should go roughly like this:
inputfile -demux-> audiostream -read-> inPackets -decode2frames->
frames
-encode2packets-> outPackets -write-> audiostream -mux-> outputfile
However I found the following comment in demuxing.c:
/* Write the raw audio data samples of the first plane. This works
* fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However,
* most audio decoders output planar audio, which uses a separate
* plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).
* In other words, this code will write only the first audio channel
* in these cases.
* You should use libswresample or libavfilter to convert the frame
* to packed data. */
My questions about this are:
Can I expect a frame that was retrieved by calling one of the decoder functions, f.e.
avcodec_decode_audio4 to hold suitable values to directly put it into an encoder or is
the resampling step mentioned in the comment mandatory?
Am I taking the right approach? ffmpeg is very asymmetric, i.e. if there is a function
open_file_for_input there might not be a function open_file_for_output. Also there are different versions of many functions (avcodec_decode_audio[1-4]) and different naming
schemes, so it's very hard to tell, if the general approach is right, or actually an
ugly mixture of techniques that where used at different version bumps of ffmpeg.
ffmpeg uses a lot of specific terms, like 'planar sampling' or 'packed format' and I am having a hard time, finding definitions for these terms. Is it possible to write working code, without deep knowledge of audio?
Here is my code so far that right now crashes at avcodec_encode_audio2
and I don't know why.
int Java_com_fscz_ffmpeg_Audio_convert(JNIEnv * env, jobject this, jstring jformat, jstring jcodec, jstring jsource, jstring jdest) {
jboolean isCopy;
jclass configClass = (*env)->FindClass(env, "com.fscz.ffmpeg.Config");
jfieldID fid = (*env)->GetStaticFieldID(env, configClass, "ffmpeg_logging", "I");
logging = (*env)->GetStaticIntField(env, configClass, fid);
/// open input
const char* sourceFile = (*env)->GetStringUTFChars(env, jsource, &isCopy);
AVFormatContext* pInputCtx;
AVStream* pInputStream;
open_input(sourceFile, &pInputCtx, &pInputStream);
// open output
const char* destFile = (*env)->GetStringUTFChars(env, jdest, &isCopy);
const char* cformat = (*env)->GetStringUTFChars(env, jformat, &isCopy);
const char* ccodec = (*env)->GetStringUTFChars(env, jcodec, &isCopy);
AVFormatContext* pOutputCtx;
AVOutputFormat* pOutputFmt;
AVStream* pOutputStream;
open_output(cformat, ccodec, destFile, &pOutputCtx, &pOutputFmt, &pOutputStream);
/// decode/encode
error = avformat_write_header(pOutputCtx, NULL);
DIE_IF_LESS_ZERO(error, "error writing output stream header to file: %s, error: %s", destFile, e2s(error));
AVFrame* frame = avcodec_alloc_frame();
DIE_IF_UNDEFINED(frame, "Could not allocate audio frame");
frame->pts = 0;
LOGI("allocate packet");
AVPacket pktIn;
AVPacket pktOut;
LOGI("done");
int got_frame, got_packet, len, frame_count = 0;
int64_t processed_time = 0, duration = pInputStream->duration;
while (av_read_frame(pInputCtx, &pktIn) >= 0) {
do {
len = avcodec_decode_audio4(pInputStream->codec, frame, &got_frame, &pktIn);
DIE_IF_LESS_ZERO(len, "Error decoding frame: %s", e2s(len));
if (len < 0) break;
len = FFMIN(len, pktIn.size);
size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format);
LOGI("audio_frame n:%d nb_samples:%d pts:%s\n", frame_count++, frame->nb_samples, av_ts2timestr(frame->pts, &(pInputStream->codec->time_base)));
if (got_frame) {
do {
av_init_packet(&pktOut);
pktOut.data = NULL;
pktOut.size = 0;
LOGI("encode frame");
DIE_IF_UNDEFINED(pOutputStream->codec, "no output codec");
DIE_IF_UNDEFINED(frame->nb_samples, "no nb samples");
DIE_IF_UNDEFINED(pOutputStream->codec->internal, "no internal");
LOGI("tests done");
len = avcodec_encode_audio2(pOutputStream->codec, &pktOut, frame, &got_packet);
LOGI("encode done");
DIE_IF_LESS_ZERO(len, "Error (re)encoding frame: %s", e2s(len));
} while (!got_packet);
// write packet;
LOGI("write packet");
/* Write the compressed frame to the media file. */
error = av_interleaved_write_frame(pOutputCtx, &pktOut);
DIE_IF_LESS_ZERO(error, "Error while writing audio frame: %s", e2s(error));
av_free_packet(&pktOut);
}
pktIn.data += len;
pktIn.size -= len;
} while (pktIn.size > 0);
av_free_packet(&pktIn);
}
LOGI("write trailer");
av_write_trailer(pOutputCtx);
LOGI("end");
/// close resources
avcodec_free_frame(&frame);
avcodec_close(pInputStream->codec);
av_free(pInputStream->codec);
avcodec_close(pOutputStream->codec);
av_free(pOutputStream->codec);
avformat_close_input(&pInputCtx);
avformat_free_context(pOutputCtx);
return 0;
}
Meanwhile I have figured this out and written an Android Library Project that does this
(for audio files). https://github.com/fscz/FFmpeg-Android
See the file /jni/audiodecoder.c for details

av_read_frame function in ffmpeg in android always returning packet.stream_index as 0

I am using the following standard code pasted below (ref: http://dranger.com/ffmpeg/) to use ffmpeg in android using ndk. My code is working fine in ubuntu 10.04 using gcc compiler. But I am facing an issue in android.The issue is av_read_frame(pFormatCtx, &packet) is always returning packet.stream_index=0. I have tested my code with various rtsp urls and I have the same behaviour in all cases. I do not have any linking or compiling issues as everything seems to be working fine except this issue. I am trying to solve this from last 2 days but I am stuck badly.Please point me in right direction.
#include <jni.h>
#include <android/log.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <stdio.h>
#define DEBUG_TAG "mydebug_ndk"
jint Java_com_example_tut2_MainActivity_myfunc(JNIEnv * env, jobject this,jstring myjurl) {
AVFormatContext *pFormatCtx = NULL;
int i, videoStream;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVPacket packet;
int frameFinished;
AVDictionary *optionsDict = NULL;
struct SwsContext *sws_ctx = NULL;
jboolean isCopy;
const char * mycurl = (*env)->GetStringUTFChars(env, myjurl, &isCopy);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:p2: [%s]", mycurl);
// Register all formats and codecs
av_register_all();
avformat_network_init();
// Open video file
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:before_open");
if(avformat_open_input(&pFormatCtx, mycurl, NULL, NULL)!=0)
return -1;
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "start: %d\t%d\n",pFormatCtx->raw_packet_buffer_remaining_size,pFormatCtx->max_index_size);
(*env)->ReleaseStringUTFChars(env, myjurl, mycurl);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:before_stream");
// Retrieve stream information
if(avformat_find_stream_info(pFormatCtx, NULL)<0)
return -1;
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:after_stream");
// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoStream=i;
break;
}
if(videoStream==-1)
return -1; // Didn't find a video stream
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:after_videostream");
// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:after_codec_context");
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:after_decoder");
if(pCodec==NULL)
return -1;
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:found_decoder");
// Open codec
if(avcodec_open2(pCodecCtx, pCodec, &optionsDict)<0)
return -1;
// Allocate video frame
pFrame=avcodec_alloc_frame();
sws_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,
pCodecCtx->height,PIX_FMT_YUV420P,SWS_BILINEAR,NULL,NULL,NULL);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:before_while");
int count=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "entered while: %d %d %d\n", packet.duration,packet.stream_index,packet.size);
if(packet.stream_index==videoStream) {
// Decode video frame
//break;
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,&packet);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:in_while");
if(frameFinished) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:gng_out_of_while");
break;
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
if(++count>1000)
return -2; //infinite while loop
}
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:after_while");
// Free the YUV frame
av_free(pFrame);
// Close the codec
avcodec_close(pCodecCtx);
// Close the video file
avformat_close_input(&pFormatCtx);
return 0;
}
I have same problam too.
after my test, I'v found my mistake, Hopes can help you.
my av_read_frame() also always return av_pkg of stream index 0;
The problem is: I mixed two version of ffmpeg libs.
At before, I'v install the ffmpeg of 0.9.x
Now, I install ffmpeg of 1.2
the libs are in different version.I used some lib from 0.9.x and others from 1.2.
So... error occur.
I solution:
uninstall all version of ffmpeg , and then reinstall one version of ffmpeg(me->1.2).
The av_read_frame() goes right. :)

Get "CURLE_COULDNT_RESOLVE_HOST" error in Android

I have compiled static libcurl for android but continuously receiving the CurlRes code 6 i.e. CURLE_COULDNT_RESOLVE_HOST.
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Curl
LOCAL_SRC_FILES := prebuild/libcurl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ccSharedLib
LOCAL_SRC_FILES := main-jni.cpp
LOCAL_STATIC_LIBRARIES := Curl
include $(BUILD_SHARED_LIBRARY)
main-jni.cpp
extern "C" {
size_t write_data(void *ptr, size_t size, size_t count, FILE *stream)
{
size_t written;
written = fwrite(ptr, size, count, stream);
printf("data sent, size = %lu",written);
return written;
}
jint
Java_com_example_testlibcurl_MainActivity_test1( JNIEnv* env,
jobject thiz, jstring downloadDirectoryPath)
{
CURLcode res;
res = curl_global_init(CURL_GLOBAL_ALL);
jint temp = 3;
printf("Method called");
const char *nativeDownloadDirPath = env->GetStringUTFChars(downloadDirectoryPath,0);
// Test code for calling methods of libCURL
CURL *curl;
FILE *fp;
std::string s = "http://travel.paintedstork.com/blog/wp-content/uploads/2012/10/2013-calendar-images-1.jpg";
curl = curl_easy_init();
if(curl)
{
fp = fopen(nativeDownloadDirPath, "wb");
res = curl_easy_setopt(curl, CURLOPT_URL, s.c_str());
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if(fp)
fclose(fp);
}
return res;
}
}
This code is downloading the image from a web source, but every time when "curl_easy_perform" method is called it gives the error code 6. I have checked this with different URL but still unsuccessful :( ...
"android.permission.INTERNET" and "android.permission.WRITE_EXTERNAL_STORAGE" permissions already given in Manifest file.
Any pointer to solve this will be a great help.
Make sure that you pass a valid file name and writable directory path to fopen.
I was getting
Fatal signal 11 (SIGSEGV) at 0x00000010 (code=1) ...
because fopen was trying to open a directory instead of a file.
Check the logs in LogCat from Eclipse or using adb logcat for other possible errors.
I've tested your code with the following modifications and it works.
MainActivity.java:
public class MainActivity
{
private static final String TAG = MainActivity.class.getName();
static
{
try
{
System.loadLibrary("mynativelib");
}
catch (UnsatisfiedLinkError ex)
{
Log.e(TAG, "WARNING: Could not load native library: " + ex.getMessage());
}
}
public static native int DownloadFile(String downloadDirectoryPath);
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int res = DownloadFile(Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + Environment.DIRECTORY_DOWNLOADS + File.separator
+ "test.jpg");
Log.d(TAG, "Result Code: " + res);
}
}
main-jni.cpp
#include <jni.h>
#include <android/log.h>
#include <string>
#include <curl/curl.h>
#define LOG_TAG "native"
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOG_WARN(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOG_DEBUG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#ifdef __cplusplus
extern "C"
{
#endif
// [FIX for Android 4.2.x]
// "WARNING: Could not load native library:
// Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol "__exidx_end" referenced by"
// http://stackoverflow.com/a/14501998/313113
void __exidx_start()
{
}
void __exidx_end()
{
}
size_t write_data(void *ptr, size_t size, size_t count, FILE *stream)
{
size_t written;
written = fwrite(ptr, size, count, stream);
LOG_DEBUG("Writing data to file stream %u", written);
return written;
}
jint Java_com_company_awesomeapp_MainActivity_DownloadFile(JNIEnv* env, jobject thiz,
jstring downloadDirectoryPath)
{
CURLcode res;
res = curl_global_init(CURL_GLOBAL_ALL);
jint temp = 3;
LOG_DEBUG("Downloading file");
const char *nativeDownloadDirPath = env->GetStringUTFChars(downloadDirectoryPath, 0);
LOG_DEBUG(nativeDownloadDirPath);
CURL *curl;
FILE *fp;
std::string url = "http://travel.paintedstork.com/blog/wp-content/uploads/2012/10/2013-calendar-images-1.jpg";
curl = curl_easy_init();
if (curl)
{
fp = fopen(nativeDownloadDirPath, "wb");
res = curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
LOG_DEBUG("Before write function");
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
LOG_DEBUG("After write function");
LOG_DEBUG("Before write data");
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
LOG_DEBUG("After write data");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (fp) fclose(fp);
}
env->ReleaseStringUTFChars(downloadDirectoryPath, nativeDownloadDirPath);
return res;
}
/**
* The VM calls JNI_OnLoad when the native library is loaded (for example, through System.loadLibrary).
* JNI_OnLoad must return the JNI version needed by the native library.
*
* #see http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#JNI_OnLoad
*/JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
{
return -1;
}
// [*] Get jclass with env->FindClass.
// [*] Register methods with env->RegisterNatives.
//jniRegisterNativeMethods(env, "dev/android/sample/AndroidNDKSampleActivity", sMethods, NELEM(sMethods));
return JNI_VERSION_1_6;
}
/**
* The VM calls JNI_OnUnload when the class loader containing the native library is garbage collected.
* This function can be used to perform cleanup operations.
*
* Because this function is called in an unknown context (such as from a finalizer),
* the programmer should be conservative on using Java VM services, and refrain from arbitrary
* Java call-backs.
* #see http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#JNI_OnUnload
*/JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved)
{
}
#ifdef __cplusplus
}
#endif
The file will be saved at: /storage/emulated/0/Download/test.jpg
This can also happen if you compile libcurl without threaded resolver (CMake option ENABLE_THREADED_RESOLVER) while targeting Android API level less than 23.
The problem is because implementation of CMake/CurlTests.c contains this line:
#ifndef gethostbyaddr_r
(void)gethostbyaddr_r;
#endif
However, if you check the netdb.h header in Android NDK, you will see
#if __ANDROID_API__ >= 23
int gethostbyaddr_r(const void* __addr, socklen_t __length, int __type, struct hostent* __ret, char* __buf, size_t __buf_size, struct hostent** __result, int* __h_errno_ptr) __INTRODUCED_IN(23);
#endif /* __ANDROID_API__ >= 23 */
This means that this function is not available when targeting Android older than Marshmallow. This is problematic, as testing for HAVE_GETHOSTBYNAME_R succeeds, while testing for HAVE_GETHOSTBYNAME_R_6 fails (but should succeed) with following error (observable in CMakeError.log):
/path/to/curl/CMake/CurlTests.c:128:9: error: use of undeclared identifier 'gethostbyaddr_r'
(void)gethostbyaddr_r;
^
1 error generated.
This then results with no-op behaviour in function Curl_ipv4_resolve_r in file hostip4.c, as it expects that after HAVE_GETHOSTBYNAME_R is defined, that at least one of HAVE_GETHOSTBYNAME_R_5, HAVE_GETHOSTBYNAME_R_6 or HAVE_GETHOSTBYNAME_R_3. Therefore, curl doesn't even try to resolve the host.
To fix this problem, simply remove those problematic lines from CMake/CurlTest.c.
I didn't try building curl with configure script, as in official documentation, but even if you do, it clearly states that you should target Android M or above if you plan on supporting SSL via OpenSSL, but AFAIK for simple HTTP support it should work.
The problem is gone if you use threaded resolver, as it uses different system API for performing the DNS resolution (getaddrinfo in function Curl_getaddrinfo_ex in curl_addrinfo.c). getaddrinfo is more modern API, but may not be available on all platform curl targets, such as embedded systems, which may also lack thread support. Therefore, curl uses the traditional approach in DNS resolution when threads are disabled.
P.S. Sorry for resurrecting this question after more than 7 years, but after facing the exact same problem as you, this StackOverflow question was the only similar Google search result 😛

Categories

Resources