From 912af179748912e3488dd12e963f88a06a9d1c6c Mon Sep 17 00:00:00 2001 From: Peng Li Date: Tue, 8 May 2018 10:59:55 +0800 Subject: [PATCH] capture video on usb camera and encode with libx264 --- app/src/main/AndroidManifest.xml | 1 - app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java | 1 + .../java/ai/suanzi/rtmpclient/MainActivity.java | 3 +- app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp | 218 ++++++++++++++++++++- app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h | 8 + 5 files changed, 226 insertions(+), 5 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e954746..19ba5b3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,5 +22,4 @@ - \ No newline at end of file diff --git a/app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java b/app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java index 2f7273d..ef9063a 100644 --- a/app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java +++ b/app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java @@ -26,4 +26,5 @@ public class Ffmpeg { public native int close(); public native int process(byte[] data); public native int play(Object surface, String fname); + public native int push(Object surface); } diff --git a/app/src/main/java/ai/suanzi/rtmpclient/MainActivity.java b/app/src/main/java/ai/suanzi/rtmpclient/MainActivity.java index abef9c8..ca06f1f 100644 --- a/app/src/main/java/ai/suanzi/rtmpclient/MainActivity.java +++ b/app/src/main/java/ai/suanzi/rtmpclient/MainActivity.java @@ -87,7 +87,8 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal @Override public void onClick(View view){ Log.e(TAG, "onclick2"); - ffmpeg.play(mHolder.getSurface(),"/storage/sdcard0/output.flv"); + //ffmpeg.play(mHolder.getSurface(),"/storage/sdcard0/output.flv"); + ffmpeg.push(mHolder.getSurface()); } diff --git a/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp b/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp index 7b16b49..e87fab1 100644 --- a/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp +++ b/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp @@ -37,7 +37,31 @@ void custom_log(void *ptr, int level, const char* fmt, va_list vl){ //To Logcat - LOGE(fmt, vl); + // LOGE(fmt, vl); + + + static int print_prefix = 1; + static int count; + static char prev[1024]; + char line[1024]; + static int is_atty; + + av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix); + + strcpy(prev, line); + //sanitize((uint8_t *)line); + + if (level <= AV_LOG_WARNING) + { + LOGE("%s", line); + } + else + { + LOGE("%s", line); + } + + + } @@ -73,8 +97,8 @@ JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_init (JNIEnv *env, jobje //const char* out_path = "/storage/emulated/0/Movies/output.flv"; - //const char* out_path = "rtmp://192.168.1.35:1935/myapp/suanzi"; - const char* out_path = "/storage/sdcard0/output.flv"; + const char* out_path = "rtmp://192.168.1.35:1935/myapp/suanzi"; + // const char* out_path = "/storage/sdcard0/output.flv"; @@ -436,4 +460,192 @@ LOGE("============= %d ========",__LINE__); env->ReleaseStringUTFChars(fname, file_name); return 0; +} + +JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_push (JNIEnv *env, jobject obj, jobject surface) { + + av_log_set_level(AV_LOG_TRACE); + av_register_all(); + avformat_network_init(); + avdevice_register_all(); + + LOGE("====push====="); + av_log_set_callback(custom_log); + + int ret = 0; + /// Open Input + AVFormatContext *pFormatCtx = avformat_alloc_context(); + + AVInputFormat *ifmt = av_find_input_format("video4linux2"); + if(avformat_open_input(&pFormatCtx, "/dev/video0", ifmt, NULL) != 0) { + LOGE("could not open file11"); + return -1; + } + + if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { + LOGE( "could not find stream info"); + return -1; + } + + av_dump_format(pFormatCtx, 0, "0", 0); + + AVCodec *dec; + int video_index = -1; + if((video_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0)) < 0){ + LOGE( "error"); + return -1; + } + + // avcodec_alloc_context3() + AVCodecContext *pCodecCtx = pFormatCtx->streams[video_index]->codec; + if(avcodec_open2(pCodecCtx, dec, NULL) <0){ + LOGE( "eee"); + return -1; + } + + + // Open Output + //const char* out_path = "./abc.flv"; + const char* out_path = "rtmp://192.168.1.35:1935/myapp/peng2"; + // const char* out_path = "/storage/sdcard0/output222.flv"; + + AVFormatContext *ofmt_ctx; + avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_path); + AVCodec *oDec = avcodec_find_encoder(AV_CODEC_ID_H264); + if (!oDec) { + LOGE("Can not find endoder"); + return -1; + } + + AVCodecContext *oCodecCtx = avcodec_alloc_context3(oDec); + oCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; + oCodecCtx->width = pCodecCtx->width; + oCodecCtx->height = pCodecCtx->height; + oCodecCtx->time_base.num = 1; + oCodecCtx->time_base.den = 30; + oCodecCtx->bit_rate = 800000; + oCodecCtx->gop_size = 300; + if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) + oCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; + oCodecCtx->qmin = 10; + oCodecCtx->qmax = 51; + oCodecCtx->max_b_frames = 3; + + AVDictionary *params = 0; + av_dict_set(¶ms, "preset", "ultrafast", 0); + av_dict_set(¶ms, "tune", "zerolatency", 0); + + if (avcodec_open2(oCodecCtx, oDec, ¶ms) < 0){ + LOGE("Failed to open encoder"); + return -1; + } + + AVStream *videoStream = avformat_new_stream(ofmt_ctx, oDec); + if (videoStream == NULL){ + return -1; + } + + videoStream->time_base.num = 1; + videoStream->time_base.den = 30; + videoStream->codec = oCodecCtx; + + if((ret = avio_open(&ofmt_ctx->pb, out_path, AVIO_FLAG_READ_WRITE)) < 0){ + LOGE("Failed open out file22 erro=%d, ==%s==", ret, av_err2str(ret) ); + //LOGE("Failed open out file22 erro=%d", ret); + return -1; + } + + avformat_write_header(ofmt_ctx, NULL); + ///////////// + + + + + // + AVFrame *pFrame, *pFrameYUV; + pFrame = av_frame_alloc(); + pFrameYUV = av_frame_alloc(); + + int num_bytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1); + uint8_t *buffer = (uint8_t *)av_malloc(num_bytes * sizeof(uint8_t)); + av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1); + + pFrameYUV->format = AV_PIX_FMT_YUV420P; + pFrameYUV->width = pCodecCtx->width; + pFrameYUV->height = pCodecCtx->height; + + struct SwsContext *img_convert_ctx; + img_convert_ctx = sws_getContext(pCodecCtx->width, + pCodecCtx->height, + pCodecCtx->pix_fmt, + pCodecCtx->width, + pCodecCtx->height, + AV_PIX_FMT_YUV420P, + SWS_BICUBIC, + NULL, NULL, NULL); + + AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket)); + int got_picture = 0; + + AVPacket enc_pkt ; + + int64_t framecnt = 0; + + while(av_read_frame(pFormatCtx, packet) >= 0){ + if (packet->stream_index == video_index){ + ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); + if (ret < 0){ + LOGE("Decode Error."); + return -1; + } + if (got_picture){ + sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); + + enc_pkt.data = NULL; + enc_pkt.size = 0; + av_init_packet(&enc_pkt); + int enc_got_frame = 0; + ret = avcodec_encode_video2(oCodecCtx, &enc_pkt, pFrameYUV, &enc_got_frame); + if (enc_got_frame == 1){ + + framecnt++; + enc_pkt.stream_index = videoStream->index; + + // write PTS + AVRational time_base = ofmt_ctx->streams[0]->time_base; + AVRational r_framerate1 = {60, 2}; + AVRational time_base_q = {1, AV_TIME_BASE}; + + int64_t calc_duration = (double)(AV_TIME_BASE)*(1 / av_q2d(r_framerate1)); //内部时间戳 + enc_pkt.pts = av_rescale_q(framecnt*calc_duration, time_base_q, time_base); + enc_pkt.dts = enc_pkt.pts; + enc_pkt.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)); + enc_pkt.pos = -1; + + int64_t pts_time = av_rescale_q(enc_pkt.dts, time_base, time_base_q); + + } + + + ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt); + //av_frame_free(&pFrameYUV); + //av_packet_unref(packet); + + //av_free_packet(&enc_pkt); + + /* + int y_size = pCodecCtx->width * pCodecCtx->height; + fwrite(pFrameYUV->data[0], 1, y_size, fp); // Y + fwrite(pFrameYUV->data[1], 1, y_size / 4, fp); // U + fwrite(pFrameYUV->data[2], 1, y_size / 4, fp); // V + */ + } + } + av_packet_unref(packet); + } + + sws_freeContext(img_convert_ctx); + av_free(pFrameYUV); + avcodec_close(pCodecCtx); + avformat_close_input(&pFormatCtx); } \ No newline at end of file diff --git a/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h b/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h index 6615073..5b2becd 100644 --- a/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h +++ b/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h @@ -55,6 +55,14 @@ JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_process JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_play (JNIEnv *, jobject, jobject, jstring); +/* + * Class: ai_suanzi_rtmpclient_Ffmpeg + * Method: push + * Signature: (Ljava/lang/Object;)I + */ +JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_push + (JNIEnv *, jobject, jobject); + #ifdef __cplusplus } #endif -- 2.11.0