clean the code, and fix bug service stopped when back pressed
[rtmpclient.git] / app / src / main / jni / FfmpegHelper.cpp
1 #include "FfmpegHelper.h"
2 #include "log.h"
3 #include <string>
4
5 #define FLOGE(...) av_log(NULL, AV_LOG_ERROR, __VA_ARGS__)
6 #define FLOGD(...) av_log(NULL, AV_LOG_INFO, __VA_ARGS__)
7
8 FfmpegHelper* FfmpegHelper::singleton = NULL;
9 bool FfmpegHelper::isInit = false;
10 bool FfmpegHelper::isEncoderReady = false;
11
12
13 FfmpegHelper::FfmpegHelper(JavaVM *vm, jclass cls)
14 : jvm(vm)
15 , ai_suanzi_rtmpclient_FfmpegHelper(cls)
16 {
17 }
18
19
20 jint FfmpegHelper::nativeOnLoad(JavaVM * vm, void* reserved)
21 {
22     JNIEnv* env;
23     if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK){
24         return -1;
25     }
26
27     jclass local_ref = 0;
28     if (env) local_ref = env->FindClass ("ai/suanzi/rtmpclient/FfmpegHelper");
29     jclass global_ref = reinterpret_cast<jclass> (env->NewGlobalRef (local_ref));
30     singleton = new FfmpegHelper(vm, global_ref);
31     return JNI_VERSION_1_6;
32 }
33
34 void FfmpegHelper::av_log_cb (void *ptr, int level, const char* fmt, va_list vl)
35
36     static int print_prefix = 1;
37     char line[1024];
38     av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix);
39
40     if (level <= AV_LOG_WARNING){
41         if (singleton) singleton->javaPrint(line, 1);
42     } else if(level <= AV_LOG_INFO){
43         if (singleton) singleton->javaPrint(line, 0);
44         // LOGE("%s", line);
45     } else {
46
47     }
48 }
49
50 void FfmpegHelper::javaPrint(const char* str, int level)
51 {
52     JNIEnv* env = 0;
53     if(this->jvm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK){
54         return;
55     }
56     jmethodID mid = env->GetStaticMethodID(ai_suanzi_rtmpclient_FfmpegHelper, "javaPrint", "(Ljava/lang/String;I)V");
57     jstring jstr = env->NewStringUTF(str);
58     env->CallStaticVoidMethod(ai_suanzi_rtmpclient_FfmpegHelper, mid, jstr, level);
59     env->DeleteLocalRef(jstr);
60 }
61
62 void FfmpegHelper::init()
63 {    
64     av_log_set_callback(av_log_cb);
65     av_log_set_level(AV_LOG_DEBUG);
66     FLOGD("########## Ffmpeg Init ##########");
67     unsigned int v = avutil_version();
68     FLOGD("libavutil - %d.%d.%d", AV_VERSION_MAJOR(v), AV_VERSION_MINOR(v), AV_VERSION_MICRO(v));
69     v = avcodec_version();
70     FLOGD("libavcodec - %d.%d.%d", AV_VERSION_MAJOR(v), AV_VERSION_MINOR(v), AV_VERSION_MICRO(v));
71     v = avformat_version();
72     FLOGD("libavformat - %d.%d.%d", AV_VERSION_MAJOR(v), AV_VERSION_MINOR(v), AV_VERSION_MICRO(v));
73     v = avdevice_version();
74     FLOGD("libavdevice - %d.%d.%d", AV_VERSION_MAJOR(v), AV_VERSION_MINOR(v), AV_VERSION_MICRO(v));
75
76     av_register_all();
77     //avdevice_register_all();
78     int ret = 0;
79     if((ret = avformat_network_init()) != 0){
80         FLOGE("avformat_network_init, error:%s(%d)", av_err2str(ret), ret);
81     }
82     isInit = true;
83     isEncoderReady = false;
84 }
85
86 int FfmpegHelper::initEncoder(int width, int height, const char* outpath)
87 {
88     if(!isInit) init();
89     FLOGE("-----------> FfmpegHelper::initEncoder");
90     FLOGD("initEncoder - width=%d, height=%d, url=%s", width, height, outpath);
91
92     pWidth = width;
93     pHeight = height;
94     int ret = 0;
95
96         avformat_alloc_output_context2(&formatCtx, NULL, "flv", outpath);
97
98     // initial encoder
99         if((codec = avcodec_find_encoder(AV_CODEC_ID_H264)) == NULL){
100                 FLOGE("Can not find encoder!\n");
101                 return -1;
102         }
103         codecCtx = avcodec_alloc_context3(codec);
104         codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
105         codecCtx->width = width;
106         codecCtx->height = height;
107         codecCtx->time_base.num = 1;
108         codecCtx->time_base.den = 30;
109         codecCtx->bit_rate = 800000;
110         codecCtx->gop_size = 300;
111         if (formatCtx->oformat->flags & AVFMT_GLOBALHEADER) /* Some formats want stream headers to be separate. */
112                 codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
113         //H264 codec param
114         //pCodecCtx->me_range = 16;
115         //pCodecCtx->max_qdiff = 4;
116         //pCodecCtx->qcompress = 0.6;
117         codecCtx->qmin = 10;
118         codecCtx->qmax = 51;
119         //Optional Param
120         codecCtx->max_b_frames = 3;
121         // Set H264 preset and tune
122         AVDictionary *param = 0;
123         av_dict_set(&param, "preset", "ultrafast", 0);
124         av_dict_set(&param, "tune", "zerolatency", 0);
125         if ((ret = avcodec_open2(codecCtx, codec, &param)) < 0){
126                 LOGE("Failed to open encoder!, error:%s(%d)\n", av_err2str(ret), ret);
127                 return -1;
128         }
129
130         //Add a new stream to output,should be called by the user before avformat_write_header() for muxing
131     if ((vStream = avformat_new_stream(formatCtx, codec)) == NULL){
132         FLOGE("avformat_new_stream - error");
133         return -1;
134     }
135         vStream->time_base.num = 1;
136         vStream->time_base.den = 30;
137         vStream->codec = codecCtx;
138
139         //Open output URL,set before avformat_write_header() for muxing
140         if (( ret = avio_open(&formatCtx->pb, outpath, AVIO_FLAG_READ_WRITE)) < 0){
141                 LOGE("Failed to open output file! error :%s(%d)\n", av_err2str(ret), ret);
142                 return -1;
143         }
144
145         if((ret = avformat_write_header(formatCtx, NULL)) != 0){ //Write File Header
146                 LOGE("avformat_write_header error :%s(%d)\n", av_err2str(ret), ret);
147         return -1;
148     }
149     startTime = av_gettime();
150     frameCnt = 0;
151     isEncoderReady = true;
152     return 0;
153 }
154
155
156 int FfmpegHelper::processFrame(uint8_t* data)
157 {
158     if(!isEncoderReady){
159         FLOGE("processFrame - isEncoderReady is false.");
160         return -1;
161     }
162     int ret = 0;
163     int y_length = pWidth * pHeight;
164     pFrameYUV = av_frame_alloc();
165     uint8_t *outBuf = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, codecCtx->width, codecCtx->height, 1));
166     if(outBuf == NULL) {
167         FLOGE("av_malloc, error");
168         av_frame_free(&pFrameYUV);
169         return -1;
170     }
171     if((ret = av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, outBuf, AV_PIX_FMT_YUV420P, codecCtx->width, codecCtx->height, 1)) < 0){
172         FLOGE("av_image_fill_arrays - error: %s(%d).", av_err2str(ret), ret);
173         av_frame_free(&pFrameYUV);
174         return -1;
175     }
176
177     // NV21 to YUV420P
178         memcpy(pFrameYUV->data[0], data, y_length);
179     for (int i = 0; i < y_length / 4; i++){
180         *(pFrameYUV->data[2] + i) = *(data + y_length  + i * 2);
181         *(pFrameYUV->data[1] + i) = *(data + y_length  + i * 2 + 1);
182     }
183
184         pFrameYUV->format = AV_PIX_FMT_YUV420P;
185         pFrameYUV->width = pWidth; 
186         pFrameYUV->height = pHeight;
187
188
189     encPkt.data = NULL;
190     encPkt.size = 0;
191     av_init_packet(&encPkt);
192     int got_frame = 0;
193
194         if((ret = avcodec_encode_video2(codecCtx, &encPkt, pFrameYUV, &got_frame)) < 0){
195         FLOGE("avcodec_encode_video2 - error: %s(%d).", av_err2str(ret), ret);
196         av_frame_free(&pFrameYUV);
197         return -1;
198     }
199     av_frame_free(&pFrameYUV);
200
201     if(got_frame == 1){
202         if (frameCnt % (15 * 60) == 0){
203                     FLOGD("Succeed to encode frame: %5d\tsize:%5d\n", frameCnt, encPkt.size);
204         }
205         frameCnt++;
206                 encPkt.stream_index = vStream->index;
207
208         // PTS
209                 AVRational time_base = formatCtx->streams[0]->time_base;//{ 1, 1000 };
210                 AVRational r_framerate = {60, 2 };//{ 50, 2 };
211                 AVRational time_base_q = { 1, AV_TIME_BASE };
212                 //Duration between 2 frames (us)
213                 int64_t calc_duration = (double)(AV_TIME_BASE)*(1 / av_q2d(r_framerate));       //内部时间戳
214                 //Parameters
215                 //enc_pkt.pts = (double)(framecnt*calc_duration)*(double)(av_q2d(time_base_q)) / (double)(av_q2d(time_base));
216                 encPkt.pts = av_rescale_q(frameCnt*calc_duration, time_base_q, time_base);
217                 encPkt.dts = encPkt.pts;
218                 encPkt.duration = av_rescale_q(calc_duration, time_base_q, time_base); //(double)(calc_duration)*(double)(av_q2d(time_base_q)) / (double)(av_q2d(time_base));
219                 encPkt.pos = -1;
220
221                 //Delay
222                 int64_t pts_time = av_rescale_q(encPkt.dts, time_base, time_base_q);
223                 int64_t now_time = av_gettime() - startTime;
224                 if (pts_time > now_time)
225                         av_usleep(pts_time - now_time);
226
227                 if((ret = av_interleaved_write_frame(formatCtx, &encPkt)) < 0){
228             FLOGE("av_interleaved_write_frame - error: %s(%d)", av_err2str(ret), ret);
229             return -1;
230         }
231                 av_packet_unref(&encPkt);
232     }
233     av_free(outBuf);
234     return 0;
235 }
236
237 int FfmpegHelper::close()
238 {
239     if(vStream){
240         avcodec_close(vStream->codec);
241         vStream = NULL;
242     }
243
244     if (formatCtx){
245         avio_close(formatCtx->pb);
246         avformat_free_context(formatCtx);
247         formatCtx = NULL;
248     }
249     FLOGE("<----------- FfmpegHelper::close ");
250     isEncoderReady = false;
251     return 0;
252 }
253
254
255 jint FfmpegHelper::nativeInitEncoder(JNIEnv *env, jclass cls, jint width, jint height, jstring url)
256 {
257     const char* output = env->GetStringUTFChars(url, 0);
258     int ret = 0;
259     if (singleton)  ret = singleton->initEncoder(width, height, output);
260     env->ReleaseStringUTFChars(url, output);
261     return ret;
262 }
263
264 jint FfmpegHelper::nativeProcessFrame(JNIEnv *env, jclass cls, jbyteArray data)
265 {
266         jbyte* buf = (jbyte*)env->GetByteArrayElements(data, 0);
267     int ret = 0;
268     if(singleton)  ret = singleton->processFrame((uint8_t *)buf);
269     return ret;
270 }
271
272 jint FfmpegHelper::nativeClose()
273 {
274     if(singleton) return singleton->close();
275     return 0;
276 }