ffmpeg探测网络流格式方法

windragon0419 2011-10-14 11:31:39
最近遇到好几个人在问ffmpeg如何处理网络流,刚好前段时间也在做这方面,抽空整理了下,把主要代码发出来,希望对大家有用。为简单处理,我这里只简单介绍UDP接收TS流,其实只要是socket接收的都可以类似处理。

/*
* main.c
*
* Created on: 2011-9-18
* Author: wudegang
*/

#include "utils.h"
#include <pthread.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

UdpQueue recvqueue;
UdpParam udpParam;

//注册av_read_frame的回调函数,这里只是最简处理,实际应用中应加上出错处理,超时等待...
int read_data(void *opaque, uint8_t *buf, int buf_size) {
int size = buf_size;
int ret;
// printf("read data %d\n", buf_size);
do {
ret = get_queue(&recvqueue, buf, buf_size);
} while (ret);

// printf("read data Ok %d\n", buf_size);
return size;
}

#define BUF_SIZE 4096*500

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

init_queue(&recvqueue, 1024*500);

udpParam.argv = argv;
udpParam.queue = &recvqueue;
uint8_t *buf = av_mallocz(sizeof(uint8_t)*BUF_SIZE);


//UDP接收线程
pthread_t udp_recv_thread;
pthread_create(&udp_recv_thread, NULL, udp_ts_recv, &udpParam);
pthread_detach(udp_recv_thread);



av_register_all();

AVCodec *pVideoCodec, *pAudioCodec;
AVCodecContext *pVideoCodecCtx = NULL;
AVCodecContext *pAudioCodecCtx = NULL;
AVIOContext * pb = NULL;
AVInputFormat *piFmt = NULL;
AVFormatContext *pFmt = NULL;

//step1:申请一个AVIOContext
pb = avio_alloc_context(buf, BUF_SIZE, 0, NULL, read_data, NULL, NULL);
if (!pb) {
fprintf(stderr, "avio alloc failed!\n");
return -1;
}
//step2:探测流格式
if (av_probe_input_buffer(pb, &piFmt, "", NULL, 0, 0) < 0) {
fprintf(stderr, "probe failed!\n");
return -1;
} else {
fprintf(stdout, "probe success!\n");
fprintf(stdout, "format: %s[%s]\n", piFmt->name, piFmt->long_name);
}

pFmt = avformat_alloc_context();
pFmt->pb = pb; //step3:这一步很关键
//step4:打开流
if (avformat_open_input(&pFmt, "", piFmt, NULL) < 0) {
fprintf(stderr, "avformat open failed.\n");
return -1;
} else {
fprintf(stdout, "open stream success!\n");
}
//以下就和文件处理一致了
if (av_find_stream_info(pFmt) < 0) {
fprintf(stderr, "could not fine stream.\n");
return -1;
}

av_dump_format(pFmt, 0, "", 0);

int videoindex = -1;
int audioindex = -1;
for (int i = 0; i < pFmt->nb_streams; i++) {
if ( (pFmt->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
(videoindex < 0) ) {
videoindex = i;
}
if ( (pFmt->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) &&
(audioindex < 0) ) {
audioindex = i;
}
}

if (videoindex < 0 || audioindex < 0) {
fprintf(stderr, "videoindex=%d, audioindex=%d\n", videoindex, audioindex);
return -1;
}

AVStream *pVst,*pAst;
pVst = pFmt->streams[videoindex];
pAst = pFmt->streams[audioindex];

pVideoCodecCtx = pVst->codec;
pAudioCodecCtx = pAst->codec;

pVideoCodec = avcodec_find_decoder(pVideoCodecCtx->codec_id);
if (!pVideoCodec) {
fprintf(stderr, "could not find video decoder!\n");
return -1;
}
if (avcodec_open(pVideoCodecCtx, pVideoCodec) < 0) {
fprintf(stderr, "could not open video codec!\n");
return -1;
}

pAudioCodec = avcodec_find_decoder(pAudioCodecCtx->codec_id);
if (!pAudioCodec) {
fprintf(stderr, "could not find audio decoder!\n");
return -1;
}
if (avcodec_open(pAudioCodecCtx, pAudioCodec) < 0) {
fprintf(stderr, "could not open audio codec!\n");
return -1;
}

int got_picture;
uint8_t samples[AVCODEC_MAX_AUDIO_FRAME_SIZE*3/2];
AVFrame *pframe = avcodec_alloc_frame();
AVPacket pkt;
av_init_packet(&pkt);

while(1) {
if (av_read_frame(pFmt, &pkt) >= 0) {

if (pkt.stream_index == videoindex) {
fprintf(stdout, "pkt.size=%d,pkt.pts=%lld, pkt.data=0x%x.", pkt.size, pkt.pts,(unsigned int)pkt.data);
avcodec_decode_video2(pVideoCodecCtx, pframe, &got_picture, &pkt);
if (got_picture) {
fprintf(stdout, "decode one video frame!\n");
}
}else if (pkt.stream_index == audioindex) {
int frame_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*3/2;
if (avcodec_decode_audio3(pAudioCodecCtx, (int16_t *)samples, &frame_size, &pkt) >= 0) {
fprintf(stdout, "decode one audio frame!\n");
}
}
av_free_packet(&pkt);
}
}

av_free(buf);
av_free(pframe);
free_queue(&recvqueue);
return 0;
}
...全文
13860 63 打赏 收藏 转发到动态 举报
写回复
用AI写文章
63 条回复
切换为时间正序
请发表友善的回复…
发表回复
sunday410 2014-07-05
  • 打赏
  • 举报
回复
正在做网络方面的开发,mark~
theflyfish2 2014-02-07
  • 打赏
  • 举报
回复
非常感谢楼主,最近正被这个东西困惑,先好好研究研究!
maskblue 2014-01-17
  • 打赏
  • 举报
回复
楼主好,你这个"utils.h"是?
wangzongze1015 2013-12-16
  • 打赏
  • 举报
回复
学了好东东 万分感谢 谁能帮帮我?我用ffmpeg播实时流的时候 播的时间越长延时越大。延时从哪里来的?
放猪之人 2013-12-03
  • 打赏
  • 举报
回复
请问av_read_frame的超时处理应该怎么写
sunnyrainab 2013-11-29
  • 打赏
  • 举报
回复
mark,学习了,谢谢
zhangran6868 2013-10-25
  • 打赏
  • 举报
回复
引用 20 楼 luchao002 的回复:
请问下,那个回调函数read_packet(void *opaque, uint8_t *buf, int buf_size) 这里的buf_size的大小是32*1024B,如果我接收网络流,传入的数据小于这个大小,那么执行到后面的av_probe_input_buffer函数时,报错。H264流好没有报错,我测的是MPEG4的码流。 请教下,这个是什么原因引起的呢?谢谢! 是不是需要手动组包,将收到的数据组合成每个32*1024B大小的,然后在这个回调函数里面,一块块的取,每次取32*1024B?
哥们你好啊,看到你的回复,我和你有同样的困惑,请问下你是怎么解决的呢,有机会想跟你请教下,谢谢啊,我的qq是2322282691,期待你的回复啊!
zhangran6868 2013-10-25
  • 打赏
  • 举报
回复
引用 18 楼 windragon0419 的回复:
这两天又有人发邮件问关于接收ts流的问题,本想把原先的测试源码上传的,提示成功了,但我找不到,不太会玩。是否还要经过审核?
哥们你好啊,你这个可以实现实时的解码嘛?
zhangran6868 2013-10-25
  • 打赏
  • 举报
回复
引用 18 楼 windragon0419 的回复:
这两天又有人发邮件问关于接收ts流的问题,本想把原先的测试源码上传的,提示成功了,但我找不到,不太会玩。是否还要经过审核?
哥们你好啊,可否给我发一份呢,2322282691@qq.com,非常感谢啊!
yierhan111 2013-09-26
  • 打赏
  • 举报
回复
引用 46 楼 windragon0419 的回复:
过了这么久,还有人在追楼,只不过我偶尔才来一次,所以回答各位的问题不会很及时,我留个邮箱:windragon@126.com,虽然这段时间没用ffmpeg了,如果大家在使用ffmpeg中遇到问题,欢迎讨论。
楼主好人,希望抽空回答一下,谢谢了
yierhan111 2013-09-26
  • 打赏
  • 举报
回复
楼主请教你一个问题,我编译了安卓下面FFMPEG的静态文件,在安卓下面打开RTMP的视频流,avformat_open_input的时候一直返回负数,RTMP的视频流的地址是没有错了,在其它软件下能打开,是不是安卓下面编译的静态文件的config.sh需要设置什么参数,应该怎么设置呢?
miracletaofei1 2013-07-02
  • 打赏
  • 举报
回复
按照上面方式,出现 av_read_frame函数不返回,一直在read_data里面转,怎么处理啊,谢谢!
明月惊鹊 2013-06-19
  • 打赏
  • 举报
回复
她嘛的,居然看到这么好的帖子,俺也抄袭了一段代码,从内存里读取数据的,如果有文件头给av_probe_input_buffer就是正确的,如果只有一些packet给它,就是失败的,难道直接从packet中识别不行的嘛?
xiabin1002 2013-05-22
  • 打赏
  • 举报
回复
楼主的RTP流发送写的比较详细 顶
yayexing 2012-07-11
  • 打赏
  • 举报
回复
ffmpeg+live55我完全是个菜鸟中的菜鸟,请问下各位怎么入手啊。
wk83837315 2012-06-14
  • 打赏
  • 举报
回复
楼主分享的很有帮助~~支持一下
windragon0419 2012-06-08
  • 打赏
  • 举报
回复
过了这么久,还有人在追楼,只不过我偶尔才来一次,所以回答各位的问题不会很及时,我留个邮箱:windragon@126.com,虽然这段时间没用ffmpeg了,如果大家在使用ffmpeg中遇到问题,欢迎讨论。
windragon0419 2012-06-08
  • 打赏
  • 举报
回复
[Quote=引用 42 楼 的回复:]

楼主,我运行你的代码,它只显示了接收包的序号和大小,怎么没显示任何探测失败或成功的提示?
[/Quote]

问题描述得不清,我没法回答你,有个QQ群3597082,你可以去那里讨论。
windragon0419 2012-06-08
  • 打赏
  • 举报
回复
[Quote=引用 43 楼 的回复:]

楼主很牛呀,真要找这个呢。
有个问题想问楼主:
avcodec_decode_video2解出的一帧720*480 YUV420怎么才1536个字节,更奇怪的是使用sws_scale 转换为RGB24后只有2136个字节,怎么这么小呀,而且保存成bmp文件是损坏的。

还劳烦楼主帮忙呀。
[/Quote]

第一:你是如何算解出帧的大小?用avpicture_get_size(PIX_FMT_XXX, width, height)可以得到一个帧数据大小,不管解出来的画面是什么,都是固定大小;
第二:检查解码是否成功,看下函数返回值是多少;
第三:保存成bmp文件是损坏的,看下文件格式的处理,涉及AVFormatContext。
以前写的一个简单的把一帧保存成jpg图像的函数,你可参考下
void log_picture(AVPicture *pic, int width, int height, char *filename,
int pixel_format) {
AVFormatContext *ctx;
AVFrame *frame;
AVCodec *codec;
char file[100];
uint8_t *buf;
int size;
AVPacket pkt;

sprintf(file, "%s.jpg", filename);
if (avformat_alloc_output_context2(&ctx, NULL, "image2", file) < 0) {
printf("cannot open output context");
return;
}
codec = avcodec_find_encoder(CODEC_ID_MJPEG);
if (codec == NULL) {
printf("codec not found");
return;
}
// AVStream *st = av_new_stream(ctx, 0);
AVStream *st = avformat_new_stream(ctx, NULL);
st->time_base = (AVRational) {1,25};
AVCodecContext *pCodecCtx = st->codec;
pCodecCtx->width = width;
pCodecCtx->height = height;
pCodecCtx->codec_id = CODEC_ID_MJPEG;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
if (ctx->flags & AVFMT_GLOBALHEADER
) {
st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
pCodecCtx->time_base = (AVRational) {1,25};
pCodecCtx->pix_fmt = PIX_FMT_YUVJ420P;
// av_set_pts_info(st, 4, pCodecCtx->time_base.num, pCodecCtx->time_base.den);
if (avcodec_open2(pCodecCtx, codec, NULL) < 0) {
printf("cannot open codec when writing jpeg");
return;
}
if (avio_open(&ctx->pb, file, AVIO_FLAG_WRITE) < 0) {
printf("Could not open '%s'\n", filename);
return;
}
avformat_write_header(ctx, NULL);
buf = av_malloc(10000000L);
frame = avcodec_alloc_frame();
avcodec_get_frame_defaults(frame);
avpicture_alloc((AVPicture *) frame, PIX_FMT_YUVJ420P, width, height);
frame->width = width;
frame->height = height;
struct SwsContext *pCtx = sws_getContext(width, height, pixel_format, width,
height, PIX_FMT_YUVJ420P, SWS_BILINEAR, NULL, NULL, NULL);
sws_scale(pCtx, (const uint8_t * const *) pic->data, pic->linesize, 0,
frame->height, frame->data, frame->linesize);
sws_freeContext(pCtx);
size = avcodec_encode_video(st->codec, buf, 10000000L, frame);
av_init_packet(&pkt);
pkt.data = buf;
pkt.size = size;
pkt.stream_index = st->index;
pkt.pts = frame->pts;
av_interleaved_write_frame(ctx, &pkt);
av_free(buf);
av_write_trailer(ctx);
avio_close(ctx->pb);
avpicture_free((AVPicture *) frame);
av_free(frame);
avcodec_close(st->codec);
avformat_free_context(ctx);
}
wenxueqian 2012-06-01
  • 打赏
  • 举报
回复
楼主很牛呀,真要找这个呢。
有个问题想问楼主:
avcodec_decode_video2解出的一帧720*480 YUV420怎么才1536个字节,更奇怪的是使用sws_scale 转换为RGB24后只有2136个字节,怎么这么小呀,而且保存成bmp文件是损坏的。

还劳烦楼主帮忙呀。
加载更多回复(42)

2,543

社区成员

发帖
与我相关
我的任务
社区描述
专题开发/技术/项目 多媒体/流媒体开发
社区管理员
  • 多媒体/流媒体开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧