FFMPEG 解码和编码(编码mjpeg)

以前的一个android工程, 把普通视频解码成yuv同时编成mjpeg, 把音频解码成pcm并调整参数。

初始化编码器和解码器


	av_register_all();
	if(avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0 )
	{
		LOGE("ERROR:avformat_open_input file: %s", filename);
		return -1; 
	}
	m2mjpeg_adpcm->pFormatCtx = pFormatCtx;
	if(avformat_find_stream_info(m2mjpeg_adpcm->pFormatCtx, NULL)<0){
		LOGE(" Couldn't find stream information");
		return -1; 
	}

//	av_dump_format(m2mjpeg_adpcm->pFormatCtx, 0, filename, 0);

	videoStream = -1;
	audioStream = -1;
	int found = 0;
	for(i=0; i<pFormatCtx->nb_streams; i++)
	{
		if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
			videoStream=i;
			pStream[MJA_VIDEO] = pFormatCtx->streams[i];
		}else if (pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
			audioStream = i;
		}
	}
	if(videoStream==-1){
		LOGE("Didn't find a video stream");
		return -1; 
	}

	if(audioStream==-1){
		LOGE("Didn't find a audio stream");
		return -1; 
	}
	m2mjpeg_adpcm->vdid = videoStream;
	m2mjpeg_adpcm->auid = audioStream;

配置编码器

	pCodecCtx[MJA_VIDEO]=pFormatCtx->streams[videoStream]->codec;
	// Find the decoder for the video stream
	pCodec[MJA_VIDEO]=avcodec_find_decoder(
			pCodecCtx[MJA_VIDEO]->codec_id);
	if(pCodec[MJA_VIDEO]==NULL) {
		LOGE("Unsupported codec!
");
		return -1; // Codec not found
	}
// Open VIdeo codec
	if(avcodec_open2(pCodecCtx[MJA_VIDEO], 
				pCodec[MJA_VIDEO], &optionsDict)<0){
		LOGE("Could not open codec");
		return -1; 
	}
	m2mjpeg_adpcm->pCodecCtx[MJA_VIDEO] = pCodecCtx[MJA_VIDEO];
	// Allocate video frame
	m2mjpeg_adpcm->pDecodeFrame = av_frame_alloc();
	if (m2mjpeg_adpcm->pDecodeFrame == NULL){
		LOGE("Error Allocate an AVFrame structure");
		return -1;

	}
//初始化缩放结构
	m2mjpeg_adpcm->pSwsFrame = av_frame_alloc();
	if(m2mjpeg_adpcm->pSwsFrame == NULL){
		LOGE("Error Allocate an AVFrame structure");
		return -1;
	}
	LOGI("AVFrame Format pit %dx%d", width, height);
//设置分辨率
	m2mjpeg_adpcm->width = width;
	m2mjpeg_adpcm->height = height;

	// Determine required buffer size and allocate buffer
	int numBytes=avpicture_get_size(AV_PIX_FMT_YUV420P, 
			pCodecCtx[MJA_VIDEO]->width,
			pCodecCtx[MJA_VIDEO]->height);
	uint8_t *buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
//分辨率调整
	m2mjpeg_adpcm->sws_ctx =
		sws_getContext
		(
		 pCodecCtx[MJA_VIDEO]->width,
		 pCodecCtx[MJA_VIDEO]->height,
		 pCodecCtx[MJA_VIDEO]->pix_fmt,
		 width,
		 height,
		 AV_PIX_FMT_YUVJ420P,
		 SWS_BILINEAR,
		 NULL,
		 NULL,
		 NULL
		);
	// Assign appropriate parts of buffer to image planes in  AVPicture
	avpicture_fill((AVPicture *)m2mjpeg_adpcm->pSwsFrame, 
			buffer, AV_PIX_FMT_YUVJ420P,
			width, height);

//音频解码器
	pStream[MJA_AUDIO] = pFormatCtx->streams[audioStream];
	m2mjpeg_adpcm->pCodecCtx[MJA_AUDIO] = pCodecCtx[MJA_AUDIO] = pStream[MJA_AUDIO]->codec;
	pCodec[MJA_AUDIO] = avcodec_find_decoder(
			m2mjpeg_adpcm->pCodecCtx[MJA_AUDIO]->codec_id) ;
	if(pCodec[MJA_AUDIO] != NULL){
		if(avcodec_open2(m2mjpeg_adpcm->pCodecCtx[MJA_AUDIO], 
					pCodec[MJA_AUDIO], NULL)<0){
			LOGE("Could not open codec");
			return -1; 
		}
	}else{
		LOGE("NO AUDIO DATA");
		return -1; 
	}

//获取MJPEG编码器
	AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
	if (codec == NULL){
		LOGE("ERROR:EnCoder Codec not found");
		return -1;
	}
	m2mjpeg_adpcm->pEnCodec = codec;
//申请Context	
	AVCodecContext *ctx = avcodec_alloc_context3(m2mjpeg_adpcm->pEnCodec);
	if (!ctx){
		LOGE("ERROR: alloc encode context failed");
		return -1;
	}
	ctx->bit_rate = bitrate;
	ctx->width = width;
	ctx->height = height;
	ctx->time_base = (AVRational ){1, 16};
	ctx->pix_fmt = AV_PIX_FMT_YUVJ420P;

	m2mjpeg_adpcm->pSwsFrame->format = ctx->pix_fmt;
	m2mjpeg_adpcm->pSwsFrame->width = width;
	m2mjpeg_adpcm->pSwsFrame->height = height;
//打开编码器	
	if (avcodec_open2(ctx, m2mjpeg_adpcm->pEnCodec, NULL) < 0) {
		LOGE("ERROR: Could not open codec");
		return -1;
	}
	m2mjpeg_adpcm->pEnCodecCtx = ctx;
	
    int ret = av_image_alloc(m2mjpeg_adpcm->pSwsFrame->data, 
			m2mjpeg_adpcm->pSwsFrame->linesize, 
			ctx->width, ctx->height,ctx->pix_fmt, 32);
	if (ret < 0){
		LOGE("ERROR:Could not Alloc Image");
	   return -1;	
	}
//设置音频参数
	m2mjpeg_adpcm->samplerate = 32000;
	m2mjpeg_adpcm->sample_fmt = AV_SAMPLE_FMT_S16;
	m2mjpeg_adpcm->ch_layout = AV_CH_LAYOUT_STEREO;

	struct SwrContext *swr_ctx = swr_alloc();
	if (!swr_ctx){
		LOGE("ERROR:Cound alloc audio swr
");
		return -1;
	}
       av_opt_set_int(swr_ctx, "in_sample_rate", c->sample_rate, 0);
       av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt",c->sample_fmt, 0);
       av_opt_set_int(swr_ctx, "out_channel_layout", layout, 0);
       av_opt_set_int(swr_ctx, "out_sample_rate", sample_rate, 0);
       av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", sample_fmt, 0);
       av_opt_set_int(swr_ctx, "in_channel_layout",
		   	av_get_default_channel_layout(c->channels), 0);

	if (swr_init(swr_ctx) < 0){
		LOGE("ERROR:Count not init audio swr
");
		swr_free(swr_ctx);
		return -1;
	}
	*swr = swr_ctx;

      *nb_samples = av_rescale_rnd(c->frame_size, sample_rate, c->sample_rate, AV_ROUND_UP);
      *nb_channels = av_get_channel_layout_nb_channels(layout);
      LOGI("nb_samples:%d, nb_channels:%d", *nb_samples, *nb_channels);



   if(av_samples_alloc_array_and_samples(&m2mjpeg_adpcm->abuf, &m2mjpeg_adpcm->linesize,
			   m2mjpeg_adpcm->nb_channels,m2mjpeg_adpcm->nb_samples,
			   m2mjpeg_adpcm->sample_fmt, 0) < 0 ){
       LOGE("Could not allocate destination samples
");
	   return -1;
   }
	LOGI("dst_linesize:%d, dst_nb_channels:%d, dst_nb_samples:%d
", 
			m2mjpeg_adpcm->linesize, m2mjpeg_adpcm->nb_channels, m2mjpeg_adpcm->nb_samples);


编码与解码

  • 调整帧率,修改android nkd下帧率设置无效,自定义计算方式,比较粗略。
	double vfs = pMja->fps*pMja->duration/1000000;
	double vfi = 1000000/pMja->fps;
	double oldts = 1000000/pMja->src_fps;
	double newts = vfi;
	uint32_t oldcount = 0;
	uint32_t newcount = 0;
  • 设置自定义文件头
	mja_file_header_t file_header = {
		.audio_sample_rate = pMja->samplerate,
		.video_frames = (uint32_t)vfs,
		.video_frame_interval = (uint32_t)vfi,
		.crc_32 = 0,

	};
	file_header.crc_32 = crc32(&file_header, sizeof(file_header)-sizeof(uint32_t));
#if MJA_DEBUG
	//dump_file_header(&file_header);
#endif


	queue_push_data(queue, &file_header, sizeof(file_header));	
	pthread_mutex_lock(&pMja->mutex);
	pMja->ctl = MJA_START;
	pthread_mutex_unlock(&pMja->mutex);
  • 视频解码
      if(packet.stream_index==videoStream) {
		// Decode video frame
		avcodec_decode_video2(pCodecCtx[MJA_VIDEO], 
				pFDec, &frameFinished, &packet);
		// Save the frame to disk
		if (frameFinished)
		{
            double pts = av_frame_get_best_effort_timestamp(pFDec);
            pts = av_rescale_q ( pts,  *(pMja->time_base), AV_TIME_BASE_Q );
            int percent = (int)(pts/pMja->duration *100);

        //LOGI("pts: %f/%" PRId64 " = %d  ", pts, pMja->duration , percent);
        }

  • 视频编码
	sws_scale(sws_ctx, 
			(uint8_t const * const *)pFDec->data,
			pFDec->linesize,
		   	0,
		   	pCodecCtx[MJA_VIDEO]->height, 
			pFSws->data, 
			pFSws->linesize);
	ret = avcodec_encode_video2(pMja->pEnCodecCtx, &pkt,
			pFSws, &got_encode_frame);

  • 音频解码
	ret = avcodec_decode_audio4(pCodecCtx[MJA_AUDIO], 
			pFDec, &frameFinished, &packet);
	if (ret < 0){
		LOGE("ERROR:Decoding Audio Frame (%s)
", av_err2str(ret));
		break;
	}
  • 音频转换
	if (frameFinished){

		ret = swr_convert(pMja->audio_swr_ctx, abuf, pMja->nb_samples,
				(const uint8_t **)pFDec->data, pFDec->nb_samples);
		if (ret < 0) {
			LOGE( "Error while converting
");
			break;
		}
		abuf_size = av_samples_get_buffer_size(&pMja->linesize, pMja->nb_channels,
				ret, pMja->sample_fmt, 1);
		if (abuf_size < 0) {
			LOGE( "Could not get sample buffer size
");
			break;
		}
	}

详细

gist

原文地址:https://www.cnblogs.com/ikaka/p/4860858.html