ijkplayer增加截圖/錄制功能

ijkplayer是一個基于FFmpeg的輕量級Android/iOS視頻播放器芋忿。ijkplayer默認(rèn)不支持截圖和錄制功能。要想實現(xiàn)截圖、錄制功能就需要另外開發(fā)來實現(xiàn)雇逞。
本文的錄制實現(xiàn)需要ijkplayer編譯增加編碼器劣摇。ijkplayer編譯支持x264編碼器可參考:
http://www.reibang.com/p/765a71679b2a
精簡的工程目錄如下:

工程簡要目錄結(jié)構(gòu).png

android工程改成了cmake模式珠移,方便編譯、開發(fā)末融、調(diào)試钧惧,目錄結(jié)構(gòu)如下:


工程目錄結(jié)構(gòu).png

ijkplayer-java模塊修改

IjkMediaPlayer.java文件增加如下代碼:

   public native boolean getCurrentFrame(Bitmap bitmap);
    public native int startRecord(String recordVideoPath);
    public native int stopRecord();

    @Override
    public boolean doCapture(Bitmap bitmap) {
        return getCurrentFrame(bitmap);
    }

   @Override
    public boolean doStartRecord(String recordVideoPath) {
        return (startRecord(recordVideoPath)==0)?true:false;
    }

    @Override
    public void doStopRecord() {
        stopRecord();
    }

主要是jni在java層對應(yīng)的方法以及對外開放的調(diào)用方法。

native-lib模塊修改

ijkplayer_jni.c文件修改

static jboolean
IjkMediaPlayer_getCurrentFrame(JNIEnv *env, jobject thiz, jobject bitmap)
{
    jboolean retval = JNI_TRUE;
    IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
    JNI_CHECK_GOTO(mp, env, NULL, "mpjni: getCurrentFrame: null mp", LABEL_RETURN);

    uint8_t *frame_buffer = NULL;

    if (0 > AndroidBitmap_lockPixels(env, bitmap, (void **)&frame_buffer)) {
        (*env)->ThrowNew(env, "java/io/IOException", "Unable to lock pixels.");
        return JNI_FALSE;
    }

    ijkmp_get_current_frame(mp, frame_buffer);

    if (0 > AndroidBitmap_unlockPixels(env, bitmap)) {
        (*env)->ThrowNew(env, "java/io/IOException", "Unable to unlock pixels.");
        return JNI_FALSE;
    }

    LABEL_RETURN:
    ijkmp_dec_ref_p(&mp);
    return retval;
}

static jint IjkMediaPlayer_startRecord(JNIEnv* env,jobject thiz,jstring recordFilePath){
    IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
    const char *c_record_path = NULL;
    c_record_path = (*env)->GetStringUTFChars(env, recordFilePath, NULL );
    if(mp->ffplayer->dx_recordRelData.isOnEncoding == DX_RECORD_ENCODING_OFF){
        ijkmp_start_record(mp,c_record_path);
    }
    return mp->ffplayer->dx_recordRelData.isOnEncoding;
}

static jint IjkMediaPlayer_stopRecord(JNIEnv* env,jobject thiz){
    IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
    ijkmp_stop_record(mp);
    return 0;
}

static JNINativeMethod g_methods[] = {
    
        ···
    { "_setFrameAtTime",        "(Ljava/lang/String;JJII)V", (void *) IjkMediaPlayer_setFrameAtTime },
    { "getCurrentFrame",        "(Landroid/graphics/Bitmap;)Z",      (void *) IjkMediaPlayer_getCurrentFrame },
    { "startRecord",            "(Ljava/lang/String;)I",      (void *) IjkMediaPlayer_startRecord },
    { "stopRecord",            "()I",      (void *) IjkMediaPlayer_stopRecord },
};

增加如上3個函數(shù)勾习,并與java層對應(yīng)的實現(xiàn)函數(shù)進(jìn)行動態(tài)注冊關(guān)聯(lián)浓瞪。

ijkplayer.h文件修改

void ijkmp_get_current_frame(IjkMediaPlayer *mp, uint8_t *frame_buf);
void ijkmp_start_record(IjkMediaPlayer *mp, const char *recordFileName);
void ijkmp_stop_record(IjkMediaPlayer *mp);

ijkplayer.c文件修改

void ijkmp_get_current_frame(IjkMediaPlayer *mp, uint8_t *frame_buf)
{
    assert(mp);
    pthread_mutex_lock(&mp->mutex);
    ijkmp_get_current_frame_l(mp, frame_buf);
    pthread_mutex_unlock(&mp->mutex);
}

void ijkmp_start_record(IjkMediaPlayer *mp, const char *recordFileName){
    MPTRACE("ijkmp_start_record: dj prepare to start record %s\n",recordFileName);
    ffp_start_record(mp->ffplayer,recordFileName);
}

void ijkmp_stop_record(IjkMediaPlayer *mp){
            MPTRACE("ijkmp_stop_record: dj stop record\n");
    ffp_stop_record(mp->ffplayer);
}

ff_fplay.h文件修改如下:

void      ffp_get_current_frame_l(FFPlayer *ffp, uint8_t *frame_buf);
void      ffp_start_record(FFPlayer *ffp, const char *file_name);
void      ffp_stop_record(FFPlayer *ffp);

ff_ffplay.c文件修改如下:

void ffp_get_current_frame_l(FFPlayer *ffp, uint8_t *frame_buf)
{
    ALOGD("=============>start snapshot\n");

    VideoState *is = ffp->is;
    Frame *vp;
    int i = 0, linesize = 0, pixels = 0;
    uint8_t *src;

    vp = &is->pictq.queue[is->pictq.rindex];
    int height = vp->bmp->h;
    int width = vp->bmp->w;

    ALOGD("=============>%d X %d === %d\n", width, height, vp->bmp->pitches[0]);

    // copy data to bitmap in java code
    linesize = vp->bmp->pitches[0];
    src = vp->bmp->pixels[0];
    pixels = width * 4;
    for (i = 0; i < height; i++) {
        memcpy(frame_buf + i * pixels, src + i * linesize, pixels);
    }

    ALOGD("=============>end snapshot\n");
}

void ffp_start_record(FFPlayer *ffp, const char *file_name)
{
    ffp->dx_recordRelData.windex = 0;
    ffp->dx_recordRelData.rindex = 0;
    ffp->dx_recordRelData.fileName = file_name;
//    ffp->dx_recordRelData.isInRecord = DX_RECORD_STATUS_ON;
    ALOGD("ffp_start_record filename=%s recordStatus=%d\n",file_name,ffp->dx_recordRelData.isInRecord);
    ffp->dx_recordRelData.isInRecord = DX_RECORD_STATUS_ON;
    pthread_create(&(ffp->dx_recordRelData.recThreadid),NULL,doRecordFile,(void *)(&(ffp->dx_recordRelData)));
}

void ffp_stop_record(FFPlayer *ffp){
    ALOGD("ffp_stop_record filename=%s recordStatus=%d\n",ffp->dx_recordRelData.fileName,ffp->dx_recordRelData.isInRecord);
    ffp->dx_recordRelData.isInRecord = DX_RECORD_STATUS_OFF;

}

//解碼后將解碼數(shù)據(jù)保存下來
static int decoder_decode_frame(FFPlayer *ffp, Decoder *d, AVFrame *frame, AVSubtitle *sub) {
    int ret = AVERROR(EAGAIN);

    for (;;) {
        AVPacket pkt;

        if (d->queue->serial == d->pkt_serial) {
            do {
                if (d->queue->abort_request)
                    return -1;

                switch (d->avctx->codec_type) {
                    case AVMEDIA_TYPE_VIDEO:
                        ret = avcodec_receive_frame(d->avctx, frame);
                        if (ret >= 0) {
                            ffp->stat.vdps = SDL_SpeedSamplerAdd(&ffp->vdps_sampler, FFP_SHOW_VDPS_AVCODEC, "vdps[avcodec]");
                            if (ffp->decoder_reorder_pts == -1) {
                                frame->pts = frame->best_effort_timestamp;
                            } else if (!ffp->decoder_reorder_pts) {
                                frame->pts = frame->pkt_dts;
                            }

                            //mody by dj add video record process
                            if (frame->format == AV_PIX_FMT_YUV420P && ffp->dx_recordRelData.isInRecord == DX_RECORD_STATUS_ON){
                                if (frame->width >0 && frame->height >0){
                                    DX_FrameData frData;
                                    frData.data0 = (uint8_t *)malloc((size_t)frame->linesize[0] * frame->height);
                                    frData.data1 = (uint8_t *)malloc((size_t)frame->linesize[1]*frame->height/2);
                                    frData.data2 = (uint8_t *)malloc((size_t)frame->linesize[1]*frame->height/2);
                                    frData.dataNum = 3;
                                    frData.frameType = DX_FRAME_TYPE_VIDEO;
                                    frData.lineSize0 = frame->linesize[0];
                                    frData.lineSize1 = frame->linesize[1];
                                    frData.lineSize2 = frame->linesize[2];
                                    frData.format = frame->format;
                                    memcpy(frData.data0,frame->data[0],frame->linesize[0]*frame->height);
                                    memcpy(frData.data1,frame->data[1],frame->linesize[1]*frame->height/2);
                                    memcpy(frData.data2,frame->data[2],frame->linesize[2]*frame->height/2);
                                    int windex = ffp->dx_recordRelData.windex;
                                    ffp->dx_recordRelData.recordFramesQueue[windex] = frData;
                                    ffp->dx_recordRelData.windex += 1;
                                    ffp->dx_recordRelData.srcFormat.height = frame->height;
                                    ffp->dx_recordRelData.srcFormat.width = frame->width;
                                }
                            }
                        }
                        break;
                    case AVMEDIA_TYPE_AUDIO:
                        ret = avcodec_receive_frame(d->avctx, frame);
                        if (ret >= 0) {
                            AVRational tb = (AVRational){1, frame->sample_rate};
                            if (frame->pts != AV_NOPTS_VALUE)
                                frame->pts = av_rescale_q(frame->pts, av_codec_get_pkt_timebase(d->avctx), tb);
                            else if (d->next_pts != AV_NOPTS_VALUE)
                                frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
                            if (frame->pts != AV_NOPTS_VALUE) {
                                d->next_pts = frame->pts + frame->nb_samples;
                                d->next_pts_tb = tb;
                            }
                            //mody by dj add video record process
                            if(ffp->dx_recordRelData.isInRecord == DX_RECORD_STATUS_ON && frame->format == AV_SAMPLE_FMT_FLTP){
                                if(frame->linesize[0] >0){
                                    DX_FrameData frData;
                                    frData.data0 = (uint8_t *)av_malloc(frame->linesize[0]);
                                    memcpy(frData.data0,frame->data[0],frame->linesize[0]);
                                    frData.data1 = (uint8_t *)av_malloc(frame->linesize[0]);
                                    memcpy(frData.data1,frame->data[1],frame->linesize[0]);
                                    frData.frameType = DX_FRAME_TYPE_AUDIO;
                                    frData.dataNum = 2;
                                    frData.nb_samples = frame->nb_samples;
                                    frData.channel_layout = frame->channel_layout;
                                    frData.channels = frame->channels;
                                    frData.lineSize0 = frame->linesize[0];
                                    frData.lineSize1 = frame->linesize[0];
                                    frData.format = frame->format;
                                    int windex = ffp->dx_recordRelData.windex;
                                    ffp->dx_recordRelData.recordFramesQueue[windex] = frData;
                                    ffp->dx_recordRelData.windex += 1;
                                }
                            }
//                            else if(ffp->dx_recordRelData.isInRecord == DX_RECORD_STATUS_ON && frame->format == AV_SAMPLE_FMT_S16){
//                                if(frame->linesize[0] >0){
//                                    DX_FrameData frData;
//                                    frData.data0 = (uint8_t *)av_malloc(frame->linesize[0]);
//                                    memcpy(frData.data0,frame->data[0],frame->linesize[0]);
//                                    frData.frameType = DX_FRAME_TYPE_AUDIO;
//                                    frData.dataNum = 1;
//                                    frData.nb_samples = frame->nb_samples;
//                                    frData.channel_layout = frame->channel_layout;
//                                    frData.channels = frame->channels;
//                                    frData.lineSize0 = frame->linesize[0];
//                                    frData.format = frame->format;
//                                    int windex = ffp->dx_recordRelData.windex;
//                                    ffp->dx_recordRelData.recordFramesQueue[windex] = frData;
//                                    ffp->dx_recordRelData.windex += 1;
//                                }
//                            }
                        }
                        break;
                    default:
                        break;
                }
                if (ret == AVERROR_EOF) {
                    d->finished = d->pkt_serial;
                    avcodec_flush_buffers(d->avctx);
                    return 0;
                }
                if (ret >= 0)
                    return 1;
            } while (ret != AVERROR(EAGAIN));
        }

        do {
            if (d->queue->nb_packets == 0)
                SDL_CondSignal(d->empty_queue_cond);
            if (d->packet_pending) {
                av_packet_move_ref(&pkt, &d->pkt);
                d->packet_pending = 0;
            } else {
                if (packet_queue_get_or_buffering(ffp, d->queue, &pkt, &d->pkt_serial, &d->finished) < 0)
                    return -1;
            }
        } while (d->queue->serial != d->pkt_serial);

        if (pkt.data == flush_pkt.data) {
            avcodec_flush_buffers(d->avctx);
            d->finished = 0;
            d->next_pts = d->start_pts;
            d->next_pts_tb = d->start_pts_tb;
        } else {
            if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                int got_frame = 0;
                ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, &pkt);
                if (ret < 0) {
                    ret = AVERROR(EAGAIN);
                } else {
                    if (got_frame && !pkt.data) {
                       d->packet_pending = 1;
                       av_packet_move_ref(&d->pkt, &pkt);
                    }
                    ret = got_frame ? 0 : (pkt.data ? AVERROR(EAGAIN) : AVERROR_EOF);
                }
            } else {
                if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN)) {
                    av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
                    d->packet_pending = 1;
                    av_packet_move_ref(&d->pkt, &pkt);
                }
            }
            av_packet_unref(&pkt);
        }
    }
}

在ff_ffplay_def.h文件中將dj_record.h頭文件include進(jìn)來

#include "ijkmeta.h"
#include "dj_record.h"

typedef struct FFPlayer {
    const AVClass *av_class;

    /* ffplay context */
    VideoState *is;

    /* format/codec options */
    AVDictionary *format_opts;
    AVDictionary *codec_opts;
    AVDictionary *sws_dict;
    AVDictionary *player_opts;
    AVDictionary *swr_opts;
    AVDictionary *swr_preset_opts;

    /* ffplay options specified by the user */
#ifdef FFP_MERGE
    AVInputFormat *file_iformat;
#endif
    char *input_filename;
#ifdef FFP_MERGE
    const char *window_title;
    int fs_screen_width;
    int fs_screen_height;
    int default_width;
    int default_height;
    int screen_width;
    int screen_height;
#endif
    int audio_disable;
    int video_disable;
    int subtitle_disable;
    const char* wanted_stream_spec[AVMEDIA_TYPE_NB];
    int seek_by_bytes;
    int display_disable;
    int show_status;
    int av_sync_type;
    int64_t start_time;
    int64_t duration;
    int fast;
    int genpts;
    int lowres;
    int decoder_reorder_pts;
    int autoexit;
#ifdef FFP_MERGE
    int exit_on_keydown;
    int exit_on_mousedown;
#endif
    int loop;
    int framedrop;
    int64_t seek_at_start;
    int subtitle;
    int infinite_buffer;
    enum ShowMode show_mode;
    char *audio_codec_name;
    char *subtitle_codec_name;
    char *video_codec_name;
    double rdftspeed;
#ifdef FFP_MERGE
    int64_t cursor_last_shown;
    int cursor_hidden;
#endif
#if CONFIG_AVFILTER
    const char **vfilters_list;
    int nb_vfilters;
    char *afilters;
    char *vfilter0;
#endif
    int autorotate;
    int find_stream_info;
    unsigned sws_flags;

    /* current context */
#ifdef FFP_MERGE
    int is_full_screen;
#endif
    int64_t audio_callback_time;
#ifdef FFP_MERGE
    SDL_Surface *screen;
#endif

    /* extra fields */
    SDL_Aout *aout;
    SDL_Vout *vout;
    struct IJKFF_Pipeline *pipeline;
    struct IJKFF_Pipenode *node_vdec;
    int sar_num;
    int sar_den;

    char *video_codec_info;
    char *audio_codec_info;
    char *subtitle_codec_info;
    Uint32 overlay_format;

    int last_error;
    int prepared;
    int auto_resume;
    int error;
    int error_count;
    int start_on_prepared;
    int first_video_frame_rendered;
    int first_audio_frame_rendered;
    int sync_av_start;

    MessageQueue msg_queue;

    int64_t playable_duration_ms;

    int packet_buffering;
    int pictq_size;
    int max_fps;
    int startup_volume;

    int videotoolbox;
    int vtb_max_frame_width;
    int vtb_async;
    int vtb_wait_async;
    int vtb_handle_resolution_change;

    int mediacodec_all_videos;
    int mediacodec_avc;
    int mediacodec_hevc;
    int mediacodec_mpeg2;
    int mediacodec_mpeg4;
    int mediacodec_handle_resolution_change;
    int mediacodec_auto_rotate;

    int opensles;
    int soundtouch_enable;

    char *iformat_name;

    int no_time_adjust;
    double preset_5_1_center_mix_level;

    struct IjkMediaMeta *meta;

    SDL_SpeedSampler vfps_sampler;
    SDL_SpeedSampler vdps_sampler;

    /* filters */
    SDL_mutex  *vf_mutex;
    SDL_mutex  *af_mutex;
    int         vf_changed;
    int         af_changed;
    float       pf_playback_rate;
    int         pf_playback_rate_changed;
    float       pf_playback_volume;
    int         pf_playback_volume_changed;

    void               *inject_opaque;
    void               *ijkio_inject_opaque;
    FFStatistic         stat;
    FFDemuxCacheControl dcc;

    AVApplicationContext *app_ctx;
    IjkIOManagerContext *ijkio_manager_ctx;

    int enable_accurate_seek;
    int accurate_seek_timeout;
    int mediacodec_sync;
    int skip_calc_frame_rate;
    int get_frame_mode;
    GetImgInfo *get_img_info;
    int async_init_decoder;
    char *video_mime_type;
    char *mediacodec_default_name;
    int ijkmeta_delay_init;
    int render_wait_start;
    DX_RecordRelateData dx_recordRelData;
} FFPlayer;

FFPlayer中增加DX_RecordRelateData,用于保存視頻錄制時的相關(guān)信息语卤。

增加錄制主要實現(xiàn)代碼

在natvie_lib的ijkmedia/ijkplayer下增加dj_record.h和dj_record.c這2個文件追逮。
dj_record.h文件內(nèi)容如下:

//
// Created by daijun on 2019-12-08.
//

#ifndef IJKPLAYER_DJ_RECORD_H
#define IJKPLAYER_DJ_RECORD_H

#include <pthread.h>
#include <android/log.h>
//#include <string>
#include <android/native_window.h>
#include "unistd.h"
//extern "C"{
//#include "ff_ffplay_def.h"
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
#include <libavutil/imgutils.h>
#include "libswresample/swresample.h"
#include "libavutil/timestamp.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/avassert.h"
//封裝格式處理
#include "libavformat/avformat.h"
//像素處理
#include "libswscale/swscale.h"
//}

#define STREAM_DURATION   10.0
#define STREAM_FRAME_RATE 25 /* 25 images/s */
#define STREAM_PIX_FMT    AV_PIX_FMT_YUV420P /* default pix_fmt */
#define SCALE_FLAGS SWS_BICUBIC
#define AVFMT_RAWPICTURE    0x0020  //ffmpeg源碼里缺失

//自定義宏
#define DX_FRAME_TYPE_VIDEO  0
#define DX_FRAME_TYPE_AUDIO 1
#define DX_MAX_DECODE_FRAME_SIZE 3000
//錄像狀態(tài)
#define DX_RECORD_STATUS_OFF 0
#define DX_RECORD_STATUS_ON 1
//編碼狀態(tài)
#define DX_RECORD_ENCODING_OFF 0
#define DX_RECORD_ENCODING_ON 1

typedef struct OutputStream {
    AVStream *st;
    /* pts of the next frame that will be generated */
    int64_t next_pts;
    int samples_count;
    AVFrame *frame;
    AVFrame *tmp_frame;
    float t, tincr, tincr2;
    struct SwsContext *sws_ctx;
    struct SwrContext *swr_ctx;
} OutputStream;

typedef struct InputSourceInfo{
    int width, height;
}InputSourceInfo;


//存儲解碼后的音頻/視頻數(shù)據(jù)
typedef struct DX_FrameData{
    uint8_t * data0;
    uint8_t * data1;
    uint8_t * data2;
    int lineSize0;
    int lineSize1;
    int lineSize2;
    //指示有幾個data
    int dataNum;
    //幀數(shù)據(jù)類型0-視頻 1-音頻
    int frameType;
    //音頻number of audio samples (per channel)
    int nb_samples;
    uint64_t channel_layout;
    int channels;
    //代表音頻/視頻流的格式
    int format;
}DX_FrameData;

//錄像相關(guān)信息
typedef struct DX_RecordRelateData{
    //示例代碼中編碼相關(guān)
    //輸出上下文
    OutputStream video_st;
    OutputStream audio_st;
    AVFormatContext *oc;
    AVOutputFormat *fmt;
    AVCodec *audio_codec, *video_codec;

    // 錄像文件名
    const char *fileName;
    //輸入流格式
    InputSourceInfo srcFormat;
    // 保存的解碼幀
    DX_FrameData recordFramesQueue[DX_MAX_DECODE_FRAME_SIZE];
    // 與recordFramesQueue相關(guān) 可讀的索引值(即準(zhǔn)備編碼時取的索引)初始值0 (不考慮復(fù)用情況,可不用)
    int rindex;
    // 與recordFramesQueue相關(guān) 可寫的索引值(即解碼時寫入的索引)初始值0 (即保存的解碼幀個數(shù))
    int windex;
    // 是否正在進(jìn)行錄制
    int isInRecord;
    // 錄像線程id
    pthread_t recThreadid;
    // 是否正在錄制數(shù)據(jù)編碼中
    int isOnEncoding;
}DX_RecordRelateData;


void add_stream(OutputStream *ost, AVFormatContext *oc,
                AVCodec **codec,
                enum AVCodecID codec_id,InputSourceInfo inputSrcInfo);
void open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg);
void open_audio(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg);
AVFrame *dx_alloc_picture(enum AVPixelFormat pix_fmt, int width, int height);
AVFrame *alloc_audio_frame(enum AVSampleFormat sample_fmt,
                           uint64_t channel_layout,
                           int sample_rate, int nb_samples);
int write_video_frame(AVFormatContext *oc, OutputStream *ost,AVFrame * curFr);
int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt);
void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt);
int write_audio_frame(AVFormatContext *oc, OutputStream *ost,AVFrame * curFr);
void close_stream(AVFormatContext *oc, OutputStream *ost);
//執(zhí)行錄像文件
void* doRecordFile(void *infoData);
void free_record_frames(DX_RecordRelateData* recData);

#endif //IJKPLAYER_DJ_RECORD_H

dj_record.c文件內(nèi)容如下:

//
// Created by daijun on 2019-12-08.
//
#include "dj_record.h"

#define LOGD(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"dj_record",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"dj_record",FORMAT,##__VA_ARGS__);

void add_stream(OutputStream *ost, AVFormatContext *oc, AVCodec **codec,
                enum AVCodecID codec_id,InputSourceInfo inpSrcInfo) {
    AVCodecContext *c;
    int i;
    //find the encoder

    *codec = avcodec_find_encoder(codec_id);
    if (!(*codec)) {
        fprintf(stderr, "Could not find encoder for '%s'\n",
                avcodec_get_name(codec_id));
        LOGE("未找到編碼器encoder %s",avcodec_get_name(codec_id));
        exit(1);
    }
    ost->st = avformat_new_stream(oc, *codec);
    if (!ost->st) {
        fprintf(stderr, "Could not allocate stream\n");
        exit(1);
    }
    ost->st->id = oc->nb_streams-1;
    c = ost->st->codec;
    switch ((*codec)->type) {
        case AVMEDIA_TYPE_AUDIO:
            c->sample_fmt  = (*codec)->sample_fmts ?
                             (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
            c->bit_rate    = 64000;
            c->sample_rate = 44100;
            if ((*codec)->supported_samplerates) {
                c->sample_rate = (*codec)->supported_samplerates[0];
                for (i = 0; (*codec)->supported_samplerates[i]; i++) {
                    if ((*codec)->supported_samplerates[i] == 44100)
                        c->sample_rate = 44100;
                }
            }
            c->channels        = av_get_channel_layout_nb_channels(c->channel_layout);
            c->channel_layout = AV_CH_LAYOUT_STEREO;
            if ((*codec)->channel_layouts) {
                c->channel_layout = (*codec)->channel_layouts[0];
                for (i = 0; (*codec)->channel_layouts[i]; i++) {
                    if ((*codec)->channel_layouts[i] == AV_CH_LAYOUT_STEREO)
                        c->channel_layout = AV_CH_LAYOUT_STEREO;
                }
            }
            c->channels        = av_get_channel_layout_nb_channels(c->channel_layout);
            ost->st->time_base = (AVRational){ 1, c->sample_rate };
            break;
        case AVMEDIA_TYPE_VIDEO:
            c->codec_id = codec_id;
            c->bit_rate = 400000;
            //Resolution must be a multiple of two.

            c->width    = inpSrcInfo.width;
            c->height   = inpSrcInfo.height;
// timebase: This is the fundamental unit of time (in seconds) in terms
//             of which frame timestamps are represented. For fixed-fps content,
//             timebase should be 1/framerate and timestamp increments should be
//             identical to 1.

            ost->st->time_base = (AVRational){ 1, STREAM_FRAME_RATE };
            c->time_base       = ost->st->time_base;
            c->gop_size      = 12;
// emit one intra frame every twelve frames at most

            c->pix_fmt       = STREAM_PIX_FMT;
            if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
// just for testing, we also add B frames

                c->max_b_frames = 2;
            }
            if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
// Needed to avoid using macroblocks in which some coeffs overflow.
//                 This does not happen with normal video, it just happens here as
//                 the motion of the chroma plane does not match the luma plane.

                c->mb_decision = 2;
            }
            break;
        default:
            break;
    }
// Some formats want stream headers to be separate.

    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
        c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}

void open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost,
                AVDictionary *opt_arg) {
    int ret;
    AVCodecContext *c = ost->st->codec;
    AVDictionary *opt = NULL;
    av_dict_copy(&opt, opt_arg, 0);
// open the codec

    ret = avcodec_open2(c, codec, &opt);
    av_dict_free(&opt);
    if (ret < 0) {
        fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
        LOGE("dj Could not open video codec:");
        exit(1);
    }
// allocate and init a re-usable frame

    ost->frame = dx_alloc_picture(c->pix_fmt, c->width, c->height);
    if (!ost->frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
// If the output format is not YUV420P, then a temporary YUV420P
//     picture is needed too. It is then converted to the required
//     output format.

    ost->tmp_frame = NULL;
    if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
        ost->tmp_frame = dx_alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height);
        if (!ost->tmp_frame) {
            fprintf(stderr, "Could not allocate temporary picture\n");
            exit(1);
        }
    }
}


void open_audio(AVFormatContext *oc, AVCodec *codec, OutputStream *ost,
                AVDictionary *opt_arg) {
    AVCodecContext *c;
    int nb_samples;
    int ret;
    AVDictionary *opt = NULL;
    c = ost->st->codec;
// open it

    av_dict_copy(&opt, opt_arg, 0);
    ret = avcodec_open2(c, codec, &opt);
    av_dict_free(&opt);
    if (ret < 0) {
        fprintf(stderr, "Could not open audio codec: %s\n", av_err2str(ret));
        LOGE("Could not open audio codec:");
        exit(1);
    }
// init signal generator

    ost->t     = 0;
    ost->tincr = 2 * M_PI * 110.0 / c->sample_rate;
// increment frequency by 110 Hz per second

    ost->tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;
    if (c->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)
        nb_samples = 10000;
    else
        nb_samples = c->frame_size;
    ost->frame     = alloc_audio_frame(c->sample_fmt, c->channel_layout,
                                       c->sample_rate, nb_samples);
    ost->tmp_frame = alloc_audio_frame(AV_SAMPLE_FMT_FLTP, c->channel_layout,
                                       c->sample_rate, nb_samples);
// create resampler context

    ost->swr_ctx = swr_alloc();
    if (!ost->swr_ctx) {
        fprintf(stderr, "Could not allocate resampler context\n");
        exit(1);
    }
// set options

    av_opt_set_int       (ost->swr_ctx, "in_channel_count",   c->channels,       0);
    av_opt_set_int       (ost->swr_ctx, "in_sample_rate",     c->sample_rate,    0);
    av_opt_set_sample_fmt(ost->swr_ctx, "in_sample_fmt",      AV_SAMPLE_FMT_S16, 0);
    av_opt_set_int       (ost->swr_ctx, "out_channel_count",  c->channels,       0);
    av_opt_set_int       (ost->swr_ctx, "out_sample_rate",    c->sample_rate,    0);
    av_opt_set_sample_fmt(ost->swr_ctx, "out_sample_fmt",     AV_SAMPLE_FMT_FLTP,     0);
// initialize the resampling context

    if ((ret = swr_init(ost->swr_ctx)) < 0) {
        fprintf(stderr, "Failed to initialize the resampling context\n");
        exit(1);
    }
}

AVFrame* dx_alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) {
    AVFrame *picture;
    int ret;
    picture = av_frame_alloc();
    if (!picture)
        return NULL;
    picture->format = pix_fmt;
    picture->width  = width;
    picture->height = height;
// allocate the buffers for the frame data

    ret = av_frame_get_buffer(picture, 32);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate frame data.\n");
        exit(1);
    }
    return picture;
}

AVFrame* alloc_audio_frame(enum AVSampleFormat sample_fmt, uint64_t channel_layout,
                           int sample_rate, int nb_samples) {
    AVFrame *frame = av_frame_alloc();
    int ret;
    if (!frame) {
        fprintf(stderr, "Error allocating an audio frame\n");
        exit(1);
    }
    frame->format = sample_fmt;
    frame->channel_layout = channel_layout;
    frame->sample_rate = sample_rate;
    frame->nb_samples = nb_samples;
    if (nb_samples) {
        ret = av_frame_get_buffer(frame, 0);
        if (ret < 0) {
            fprintf(stderr, "Error allocating an audio buffer\n");
            exit(1);
        }
    }
    return frame;
}


int write_video_frame(AVFormatContext *oc, OutputStream *ost,AVFrame * curFr) {
    int ret;
    AVCodecContext *c;
    AVFrame *frame;
    int got_packet = 0;
    c = ost->st->codec;
//    frame = get_video_frame(ost);
    frame = curFr;

    if (oc->oformat->flags & AVFMT_RAWPICTURE) {
// a hack to avoid data copy with some raw video muxers

        AVPacket pkt;
        av_init_packet(&pkt);
        if (!frame)
            return 1;
        pkt.flags        |= AV_PKT_FLAG_KEY;
        pkt.stream_index  = ost->st->index;
        pkt.data          = (uint8_t *)frame;
        pkt.size          = sizeof(AVPicture);
        pkt.pts = pkt.dts = frame->pts;
        av_packet_rescale_ts(&pkt, c->time_base, ost->st->time_base);
        ret = av_interleaved_write_frame(oc, &pkt);
    } else {
        AVPacket pkt = { 0 };
        av_init_packet(&pkt);
// encode the image

        ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
        if (ret < 0) {
            fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
            exit(1);
        }
        if (got_packet) {
            ret = write_frame(oc, &c->time_base, ost->st, &pkt);
        } else {
            ret = 0;
        }
    }
    if (ret < 0) {
        fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret));
        exit(1);
    }
    return (frame || got_packet) ? 0 : 1;
}


int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st,
                AVPacket *pkt) {
// rescale output packet timestamp values from codec to stream timebase

    av_packet_rescale_ts(pkt, *time_base, st->time_base);
    pkt->stream_index = st->index;
// Write the compressed frame to the media file.

    log_packet(fmt_ctx, pkt);
    return av_interleaved_write_frame(fmt_ctx, pkt);
}

void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt) {
    AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
    printf("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
           av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
           pkt->stream_index);
}

// 實際編碼時需要AV_SAMPLE_FMT_FLTP的音頻采樣數(shù)據(jù)粹舵,而目前的音頻解碼后的數(shù)據(jù)已經(jīng)是AV_SAMPLE_FMT_FLTP钮孵,
// 所以就不需要再進(jìn)行音頻格式轉(zhuǎn)換。
int write_audio_frame(AVFormatContext *oc, OutputStream *ost,AVFrame * curFr) {
    AVCodecContext *c;
    AVPacket pkt = { 0 }; // data and size must be 0;
    AVFrame *frame;
    int ret;
    int got_packet;
    int dst_nb_samples;
    av_init_packet(&pkt);
    c = ost->st->codec;
//    frame = get_audio_frame(ost);
    //換成從外部傳入數(shù)據(jù)已處理好的音頻幀
    frame = curFr;
//    dst_nb_samples = curFr->nb_samples;
    if (frame) {
        if(frame->format == AV_SAMPLE_FMT_S16){ //FMT_S16進(jìn)行格式轉(zhuǎn)換
            frame->pts = ost->next_pts;
//            ost->next_pts += frame->nb_samples;
            LOGE("寫audio fmt-s16")
            // convert samples from native format to destination codec format, using the resampler

// compute destination number of samples

        dst_nb_samples = av_rescale_rnd(swr_get_delay(ost->swr_ctx, c->sample_rate) + frame->nb_samples,
                                        c->sample_rate, c->sample_rate, AV_ROUND_UP);
        av_assert0(dst_nb_samples == frame->nb_samples);
// when we pass a frame to the encoder, it may keep a reference to it
//         internally;
//         make sure we do not overwrite it here


        ret = av_frame_make_writable(ost->frame);
        if (ret < 0)
            exit(1);
// convert to destination format

        ret = swr_convert(ost->swr_ctx,
                          ost->frame->data, dst_nb_samples,
                          (const uint8_t **)frame->data, frame->nb_samples);
        if (ret < 0) {
            fprintf(stderr, "Error while converting\n");
            exit(1);
        }
        frame = ost->frame;
        ost->next_pts += dst_nb_samples;
        }else{  // AV_SAMPLE_FMT_FLTP 格式
            dst_nb_samples = curFr->nb_samples;
        }

        frame->pts = av_rescale_q(ost->samples_count, (AVRational){1, c->sample_rate}, c->time_base);
        ost->samples_count += dst_nb_samples;
    }
    ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);
    if (ret < 0) {
        fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
        exit(1);
    }
    if (got_packet) {
        ret = write_frame(oc, &c->time_base, ost->st, &pkt);
        if (ret < 0) {
            fprintf(stderr, "Error while writing audio frame: %s\n",
                    av_err2str(ret));
            exit(1);
        }
    }
    return (frame || got_packet) ? 0 : 1;
}

void close_stream(AVFormatContext *oc, OutputStream *ost) {
    avcodec_close(ost->st->codec);
    av_frame_free(&ost->frame);
    av_frame_free(&ost->tmp_frame);
    sws_freeContext(ost->sws_ctx);
    swr_free(&ost->swr_ctx);
}

// 方式2  直接使用解碼后的yuv420p數(shù)據(jù)
void* doRecordFile(void *infoData){

//    usleep(1000);
    sleep(1);
    DX_RecordRelateData *recordRelateDataPtr = (DX_RecordRelateData*)(infoData);
recordRelateDataPtr->isOnEncoding = DX_RECORD_ENCODING_ON;

    LOGE("線程中執(zhí)行C函數(shù)執(zhí)行停止執(zhí)行錄制 %s",recordRelateDataPtr->fileName);
//    free_record_frames();

    LOGE("已錄制Frame%d",recordRelateDataPtr->windex)


    const char *fileName = recordRelateDataPtr->fileName;
    //獲取保存到的輸入上下文
    //輸出上下文
    AVFormatContext *oc;
    AVOutputFormat *fmt;    //臨時變量
    OutputStream* videoStPtr;
    OutputStream* audioStPtr;
//    AVCodec *audio_codec, *video_codec;
    int ret;
    int have_video = 0, have_audio = 0;
    int encode_video = 0, encode_audio = 0;
    AVDictionary *opt = NULL;
//    av_dict_set(&opt,"profile","baseline",AV_DICT_MATCH_CASE);

    av_dict_set(&opt, "preset", "veryfast", 0); // av_opt_set(pCodecCtx->priv_data,"preset","fast",0);
    av_dict_set(&opt, "tune", "zerolatency", 0);

    LOGE("線程中avformat_alloc_output_context2");
    avformat_alloc_output_context2(&(recordRelateDataPtr->oc),NULL,NULL,fileName);
    oc = recordRelateDataPtr->oc;
    if (!oc){
        LOGD("startRecord avformat_alloc_output_context2 fail");
        avformat_alloc_output_context2(&oc, NULL, "mp4", fileName);
    }
    if (!oc){
        LOGE("avformat_alloc_output_context2 失敗");
        return NULL;
    }else{
        LOGE("avformat_alloc_output_context2 成功");
    }
    fmt = recordRelateDataPtr->oc->oformat;
    //Add the audio and video streams using the default format codecs
    // and initialize the codecs.
    LOGE("線程中add_stream");
    if (fmt->video_codec != AV_CODEC_ID_NONE) {
        add_stream(&(recordRelateDataPtr->video_st), recordRelateDataPtr->oc, &(recordRelateDataPtr->video_codec), fmt->video_codec,recordRelateDataPtr->srcFormat);
        have_video = 1;
        encode_video = 1;
    }
    if (fmt->audio_codec != AV_CODEC_ID_NONE) {
        add_stream(&(recordRelateDataPtr->audio_st), recordRelateDataPtr->oc, &recordRelateDataPtr->audio_codec, fmt->audio_codec,recordRelateDataPtr->srcFormat);
        have_audio = 1;
        encode_audio = 1;
    }
    LOGE("線程中 open video");
    videoStPtr = &recordRelateDataPtr->video_st;
    audioStPtr = &recordRelateDataPtr->audio_st;
    if (have_video)
        open_video(recordRelateDataPtr->oc, recordRelateDataPtr->video_codec, &recordRelateDataPtr->video_st, opt);
    if (have_audio)
        open_audio(recordRelateDataPtr->oc, recordRelateDataPtr->audio_codec, &recordRelateDataPtr->audio_st, opt);
    av_dump_format(recordRelateDataPtr->oc, 0, fileName, 1);
    //open the output file, if needed
    LOGE("線程中 avio_open");
    if (!(fmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&oc->pb, fileName, AVIO_FLAG_WRITE);
        if (ret < 0) {
            LOGE("Could not open %s",fileName);
            LOGE("dj Could not open %s",fileName);
            return NULL;
        }
    }
    //Write the stream header, if any.
    LOGE("線程中 avformat_write_header");

    ret = avformat_write_header(oc, &opt);
    if (ret < 0) {
        LOGE("Error occurred when opening output file %s",av_err2str(ret));
        return NULL;
    }
    InputSourceInfo inSrcInfo = recordRelateDataPtr->srcFormat;
    //v3 根據(jù)所有解碼后的幀順序(包括視頻幀和音頻幀來編碼)
    int frNum = recordRelateDataPtr->windex;
    LOGE("總共需編碼 %d 幀",frNum);
    struct SwsContext *swsContext = sws_getContext(
            inSrcInfo.width   //原圖片的寬
            ,inSrcInfo.height  //源圖高
            ,AV_PIX_FMT_YUV420P //源圖片format
            ,inSrcInfo.width  //目標(biāo)圖的寬
            ,inSrcInfo.height  //目標(biāo)圖的高
            ,AV_PIX_FMT_YUV420P,SWS_FAST_BILINEAR
            , NULL, NULL, NULL
    );

    // 音頻AV_SAMPLE_FMT_S16 轉(zhuǎn) AV_SAMPLE_FMT_FLTP
//    struct SwrContext *swrAudio = swr_alloc();
//    av_opt_set_int       (swrAudio, "in_channel_count",   AV_CH_LAYOUT_STEREO,       0);
//    av_opt_set_int       (swrAudio, "in_sample_rate",     16000,    0);
//    av_opt_set_sample_fmt(swrAudio, "in_sample_fmt",      AV_SAMPLE_FMT_S16, 0);
//    av_opt_set_int       (swrAudio, "out_channel_count",  AV_CH_LAYOUT_STEREO,       0);
//    av_opt_set_int       (swrAudio, "out_sample_rate",    16000,    0);
//    av_opt_set_sample_fmt(swrAudio, "out_sample_fmt",     AV_SAMPLE_FMT_FLTP,     0);
//    swr_init(swrAudio);
////    swrAudio = swr_alloc();
    LOGE("線程中 開始音視頻編碼");
//    for(int i=0;i<recordRelateDataPtr->recordDataFrames.size();i++){  // method 1
    for(int i=0;i<recordRelateDataPtr->windex;i++){        //method 2
//            DX_FrameData frData = recordRelateDataPtr->recordDataFrames[i]; // method 1
        DX_FrameData frData = recordRelateDataPtr->recordFramesQueue[i];    //method 2
        //        LOGE("線程中 拿到一解碼幀數(shù)據(jù)");
        // 視頻幀
        if(frData.frameType == DX_FRAME_TYPE_VIDEO){
            //對輸出圖像進(jìn)行色彩眼滤,分辨率縮放巴席,濾波處理
            uint8_t *srcSlice[3];
            srcSlice[0] = frData.data0;
            srcSlice[1] = frData.data1;
            srcSlice[2] = frData.data2;
            int srcLineSize[3];
            srcLineSize[0] = frData.lineSize0;
            srcLineSize[1] = frData.lineSize1;
            srcLineSize[2] = frData.lineSize2;
            //yuv數(shù)據(jù)每個分量一個字節(jié),一行大小等于寬度
            int dstLineSize[3];
            dstLineSize[0] = inSrcInfo.width;
            dstLineSize[1] = inSrcInfo.width/2;
            dstLineSize[2] = inSrcInfo.width/2;
            sws_scale(swsContext, (const uint8_t *const *) srcSlice, srcLineSize, 0,
                      inSrcInfo.height, recordRelateDataPtr->video_st.frame->data, recordRelateDataPtr->video_st.frame->linesize);
            recordRelateDataPtr->video_st.frame->pts = recordRelateDataPtr->video_st.next_pts++;
            //            fill_yuv_image(recordRelateDataPtr->video_st.frame,i,inSrcInfo.width,inSrcInfo.height);

            LOGE("線程中 開始處理一幀視頻數(shù)據(jù)");
            write_video_frame(oc,&recordRelateDataPtr->video_st,recordRelateDataPtr->video_st.frame);
            LOGE("線程中 完成一幀數(shù)據(jù)編碼寫入");
        }else {     //音頻幀
            //avcodec_decode_audio4  解碼出來的音頻數(shù)據(jù)是 AV_SAMPLE_FMT_FLTP,所以數(shù)據(jù)在data0 data1中
            //            LOGE("開始編碼音頻幀 nb_samples %d  channels %d  channel_layout %d",frData.nb_samples,frData.channels,frData.channel_layout);
            if(frData.format == AV_SAMPLE_FMT_FLTP){
                AVFrame* tmpFr = audioStPtr->tmp_frame;
                tmpFr->data[0] = (uint8_t *)av_malloc(frData.lineSize0);
                tmpFr->data[1] = (uint8_t *)av_malloc(frData.lineSize1);
                memcpy(tmpFr->data[0],frData.data0,frData.lineSize0);
                memcpy(tmpFr->data[1],frData.data1,frData.lineSize1);
                tmpFr->nb_samples = frData.nb_samples;
                tmpFr->channels = frData.channels;
                tmpFr->channel_layout = frData.channel_layout;
                tmpFr->pts = audioStPtr->next_pts;
                audioStPtr->next_pts += tmpFr->nb_samples;

                //            LOGE("完成單幀音頻編碼數(shù)據(jù)拷貝");
                //            LOGE("線程中 開始寫視頻幀");
                tmpFr->format = frData.format;
                write_audio_frame(oc,audioStPtr,tmpFr);
            }else if(frData.format == AV_SAMPLE_FMT_S16){   //需要音頻格式轉(zhuǎn)換
                LOGE("線程中 開始處理一幀音頻數(shù)據(jù)");
                AVFrame* tmpFr = audioStPtr->tmp_frame;
//                tmpFr->pts = audioStPtr->next_pts;
                tmpFr->nb_samples = frData.nb_samples;
                tmpFr->channels = frData.channels;
                tmpFr->channel_layout = frData.channel_layout;
                tmpFr->linesize[0] = frData.lineSize0;

//                audioStPtr->next_pts += tmpFr->nb_samples;
                // 直接拷貝數(shù)據(jù)
                tmpFr->data[0] = (uint8_t *)av_malloc(frData.lineSize0);
                memcpy(tmpFr->data[0],frData.data0,frData.lineSize0);
                tmpFr->format = frData.format;

                write_audio_frame(oc,audioStPtr,tmpFr);
            }


        }
    }
    LOGE("線程中 音視頻編碼完畢");
    free_record_frames(recordRelateDataPtr);


    //Write the trailer, if any. The trailer must be written before you
    //close the CodecContexts open when you wrote the header; otherwise
    //av_write_trailer() may try to use memory that was freed on
    //av_codec_close().

    av_write_trailer(oc);
    //Close each codec.

    if (have_video)
        close_stream(oc, videoStPtr);
    if (have_audio)
        close_stream(oc, audioStPtr);
    if (!(fmt->flags & AVFMT_NOFILE))
        //Close the output file.

        avio_closep(&oc->pb);
    //free the stream

    avformat_free_context(oc);
recordRelateDataPtr->isOnEncoding = DX_RECORD_ENCODING_OFF;
    return NULL;
}

void free_record_frames(DX_RecordRelateData* recData) {
    LOGE("C線程中執(zhí)行錄像內(nèi)存釋放");
    LOGE("recordDaraFrameQueue size=%d",recData->windex);
    //使用數(shù)組保存時的內(nèi)存釋放
    for(int i= 0;i<recData->windex;i++){
        DX_FrameData dataFrame = recData->recordFramesQueue[i];
        uint8_t *data0 = dataFrame.data0;
        uint8_t *data1 = dataFrame.data1;
        uint8_t *data2 = dataFrame.data2;

        if(dataFrame.dataNum == 1){
            if(data0 != NULL){
                free(data0);
                data0 = NULL;
            }
        }else if(dataFrame.dataNum == 2){
            if (data1 != NULL){
                free(data1);
                data1 = NULL;
            }
        }else if(dataFrame.dataNum == 3){
            if(data2 != NULL){
                free(data2);
                data2 = NULL;
            }
        }
    }
    recData->windex = 0;

}

整體實現(xiàn)就是將解碼后的視頻yuv诅需、音頻采樣數(shù)據(jù)保存起來漾唉,然后再進(jìn)行編碼,封裝成視頻文件堰塌。
android工程代碼:
https://github.com/godtrace12/ijkplayer
contrib目錄文件:
https://github.com/godtrace12/contrib

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赵刑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子场刑,更是在濱河造成了極大的恐慌般此,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牵现,死亡現(xiàn)場離奇詭異铐懊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瞎疼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門科乎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贼急,你說我怎么就攤上這事茅茂∧笃迹” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵玉吁,是天一觀的道長照弥。 經(jīng)常有香客問我,道長进副,這世上最難降的妖魔是什么这揣? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮影斑,結(jié)果婚禮上给赞,老公的妹妹穿的比我還像新娘。我一直安慰自己矫户,他們只是感情好片迅,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著皆辽,像睡著了一般柑蛇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驱闷,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天耻台,我揣著相機(jī)與錄音,去河邊找鬼空另。 笑死盆耽,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扼菠。 我是一名探鬼主播摄杂,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼循榆!你這毒婦竟也來了析恢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秧饮,失蹤者是張志新(化名)和其女友劉穎映挂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浦楣,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年咪辱,在試婚紗的時候發(fā)現(xiàn)自己被綠了振劳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡油狂,死狀恐怖历恐,靈堂內(nèi)的尸體忽然破棺而出寸癌,到底是詐尸還是另有隱情,我是刑警寧澤弱贼,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布蒸苇,位于F島的核電站,受9級特大地震影響吮旅,放射性物質(zhì)發(fā)生泄漏溪烤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一庇勃、第九天 我趴在偏房一處隱蔽的房頂上張望檬嘀。 院中可真熱鬧,春花似錦责嚷、人聲如沸鸳兽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽揍异。三九已至,卻和暖如春爆班,著一層夾襖步出監(jiān)牢的瞬間衷掷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工蛋济, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留棍鳖,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓碗旅,卻偏偏與公主長得像渡处,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子祟辟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容