1. 程式人生 > >RTSP再學習 -- 利用FFmpeg 將 rtsp 獲取H264裸流並儲存到檔案中

RTSP再學習 -- 利用FFmpeg 將 rtsp 獲取H264裸流並儲存到檔案中

既然已經可以通過 RTSP 獲取h264 裸流了。那麼通過 FFmpeg 將其儲存到檔案中怎麼做呢?

一、首先RTSP獲取 h264 裸流

我們上面兩篇文章主要講的是通過 rtsp://Your ip:554/stream_chn0.h265 播放H.265視訊流。

PS:我剛試了一下,我的 FFmpeg 程式暫時不支援 h265 ...   之前編譯的時候,只提供了 x264沒有x265

如果感興趣參看下面兩篇文章新增。

再結合之前講的,FFmepg 再學習系列,應該是沒問題的。不過好久沒有弄了,早忘了了。

那現在沒有可以播放的 H.264 視訊流了啊,怎麼辦?

二、FFmpeg 將H.264 裸流儲存到檔案

將其改改就可以了。

具體程式碼如下:

#include "stdafx.h"

#include <stdio.h>  

#define __STDC_CONSTANT_MACROS  

#ifdef _WIN32  
//Windows  
extern "C"
{
#include "libavcodec/avcodec.h"  
#include "libavformat/avformat.h"  
#include "libswscale/swscale.h"  
#include "SDL2/SDL.h"  
};
#else  
//Linux...  
#ifdef __cplusplus  
extern "C"
{
#endif  
#include <libavcodec/avcodec.h>  
#include <libavformat/avformat.h>  
#include <libswscale/swscale.h>  
#include <SDL2/SDL.h>  
#ifdef __cplusplus  
};
#endif  
#endif  

int main(int argc, char* argv[])
{

	AVFormatContext *pFormatCtx;
	int             i, videoindex;
	AVCodecContext  *pCodecCtx;
	AVCodec         *pCodec;
	AVFrame *pFrame, *pFrameYUV;
	uint8_t *out_buffer;
	AVPacket *packet;
	int ret, got_picture;


	struct SwsContext *img_convert_ctx;
	// 改成你自己的 URL
	char filepath[] = "rtsp://192.168.2.xx:8554/1"; 
	av_register_all();
	avformat_network_init();
	pFormatCtx = avformat_alloc_context();

	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0)////開啟網路流或檔案流  
	{
		printf("Couldn't open input stream.\n");
		return -1;
	}
	if (avformat_find_stream_info(pFormatCtx, NULL)<0)
	{
		printf("Couldn't find stream information.\n");
		return -1;
	}
	videoindex = -1;
	for (i = 0; i<pFormatCtx->nb_streams; i++)
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			videoindex = i;
			break;
		}
	if (videoindex == -1)
	{
		printf("Didn't find a video stream.\n");
		return -1;
	}
	pCodecCtx = pFormatCtx->streams[videoindex]->codec;
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL)
	{
		printf("Codec not found.\n");
		return -1;
	}
	if (avcodec_open2(pCodecCtx, pCodec, NULL)<0)
	{
		printf("Could not open codec.\n");
		return -1;
	}
	pFrame = av_frame_alloc();
	pFrameYUV = av_frame_alloc();
	out_buffer = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
	avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);

	//Output Info---輸出一些檔案(RTSP)資訊  
	printf("---------------- File Information ---------------\n");
	av_dump_format(pFormatCtx, 0, filepath, 0);
	printf("-------------------------------------------------\n");

	img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
		pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);


	packet = (AVPacket *)av_malloc(sizeof(AVPacket));

	FILE *fpSave;
	if ((fpSave = fopen("geth264.h264", "ab")) == NULL) //h264儲存的檔名  
		return 0;
	for (;;)
	{
		//------------------------------  
		if (av_read_frame(pFormatCtx, packet) >= 0)
		{
			if (packet->stream_index == videoindex)
			{
				fwrite(packet->data, 1, packet->size, fpSave);//寫資料到檔案中  
			}
			av_free_packet(packet);
		}
	}


	//--------------  
	av_frame_free(&pFrameYUV);
	av_frame_free(&pFrame);
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);

	return 0;
}
除錯結果顯示如下:



生成 geth264.h264 檔案,可播放。


三、工程下載

思考,這裡就有兩個問題未完成,一個就是怎麼將 H265的裸流儲存到檔案,再有怎麼儲存成其他格式比如MP4。

儲存到MP4檔案程式碼如下:

#include "stdafx.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef __cplusplus
}
#endif

AVFormatContext *i_fmt_ctx;
AVStream *i_video_stream;
AVFormatContext *o_fmt_ctx;
AVStream *o_video_stream;

int _tmain(int argc, char **argv)
{
	avcodec_register_all();
	av_register_all();
	avformat_network_init();

	/* should set to NULL so that avformat_open_input() allocate a new one */
	i_fmt_ctx = NULL;
	char rtspUrl[] = "rtsp://192.168.2.xx:8554/H264unicast";
	const char *filename = "1.mp4";
	if (avformat_open_input(&i_fmt_ctx, rtspUrl, NULL, NULL) != 0)
	{
		fprintf(stderr, "could not open input file\n");
		return -1;
	}

	if (avformat_find_stream_info(i_fmt_ctx, NULL)<0)
	{
		fprintf(stderr, "could not find stream info\n");
		return -1;
	}

	//av_dump_format(i_fmt_ctx, 0, argv[1], 0);

	/* find first video stream */
	for (unsigned i = 0; i<i_fmt_ctx->nb_streams; i++)
	{
		if (i_fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			i_video_stream = i_fmt_ctx->streams[i];
			break;
		}
	}
	if (i_video_stream == NULL)
	{
		fprintf(stderr, "didn't find any video stream\n");
		return -1;
	}

	avformat_alloc_output_context2(&o_fmt_ctx, NULL, NULL, filename);

	/*
	* since all input files are supposed to be identical (framerate, dimension, color format, ...)
	* we can safely set output codec values from first input file
	*/
	o_video_stream = avformat_new_stream(o_fmt_ctx, NULL);
	{
		AVCodecContext *c;
		c = o_video_stream->codec;
		c->bit_rate = 400000;
		c->codec_id = i_video_stream->codec->codec_id;
		c->codec_type = i_video_stream->codec->codec_type;
		c->time_base.num = i_video_stream->time_base.num;
		c->time_base.den = i_video_stream->time_base.den;
		fprintf(stderr, "time_base.num = %d time_base.den = %d\n", c->time_base.num, c->time_base.den);
		c->width = i_video_stream->codec->width;
		c->height = i_video_stream->codec->height;
		c->pix_fmt = i_video_stream->codec->pix_fmt;
		printf("%d %d %d", c->width, c->height, c->pix_fmt);
		c->flags = i_video_stream->codec->flags;
		c->flags |= CODEC_FLAG_GLOBAL_HEADER;
		c->me_range = i_video_stream->codec->me_range;
		c->max_qdiff = i_video_stream->codec->max_qdiff;
		c->qmin = i_video_stream->codec->qmin;
		c->qmax = i_video_stream->codec->qmax;
		c->qcompress = i_video_stream->codec->qcompress;
	}

	avio_open(&o_fmt_ctx->pb, filename, AVIO_FLAG_WRITE);
	avformat_write_header(o_fmt_ctx, NULL);

	int last_pts = 0;
	int last_dts = 0;
	int64_t pts, dts;
	while (1)
	{
		AVPacket i_pkt;
		av_init_packet(&i_pkt);
		i_pkt.size = 0;
		i_pkt.data = NULL;
		if (av_read_frame(i_fmt_ctx, &i_pkt) <0)
			break;
		/*
		* pts and dts should increase monotonically
		* pts should be >= dts
		*/
		i_pkt.flags |= AV_PKT_FLAG_KEY;
		pts = i_pkt.pts;
		i_pkt.pts += last_pts;
		dts = i_pkt.dts;
		i_pkt.dts += last_dts;
		i_pkt.stream_index = 0;

		//printf("%lld %lld\n", i_pkt.pts, i_pkt.dts);
		static int num = 1;
		printf("frame %d\n", num++);
		av_interleaved_write_frame(o_fmt_ctx, &i_pkt);
		//av_free_packet(&i_pkt);
		//av_init_packet(&i_pkt);
	}
	last_dts += dts;
	last_pts += pts;
	avformat_close_input(&i_fmt_ctx);
	av_write_trailer(o_fmt_ctx);
	avcodec_close(o_fmt_ctx->streams[0]->codec);
	av_freep(&o_fmt_ctx->streams[0]->codec);
	av_freep(&o_fmt_ctx->streams[0]);
	avio_close(o_fmt_ctx->pb);
	av_free(o_fmt_ctx);
	return 0;
}