c++ - Recepción de secuencias RTSP utilizando la biblioteca FFMPEG
ip-camera (4)
FFmpeg puede abrir transmisiones rtsp directamente como abrir archivos de video locales. Here hay un código de tutorial que se actualiza con la versión más reciente de FFmpeg.
Tengo una cámara IP en mi red de transmisión de video usando RTSP. He podido capturarlo y mostrarlo correctamente usando el comando ffplay:
ffplay rtsp://admin:[email protected]:7070
(con autenticacion)
Así que me gustaría lograr lo mismo usando la programación en C / C ++ usando la biblioteca ffmpeg
. Supongo que esto debe ser posible.
Así que permítanme formular dos simples preguntas:
¿Cómo recibo la transmisión en un programa C / C ++ utilizando la biblioteca FFMPEG? (solo proporcione alguna URL / tutorial, ya que Google no fue útil)
¿Cómo muestro el video recibido? (Igual aquí, alguna buena URL para dirigirme).
FWIW, he actualizado el código proporcionado por @technique para trabajar con las bibliotecas que tengo de FFMPEG 3.2.2. Esperemos que esto ayude a alguien a googlear esto. Hay algunos pequeños cambios que podrían ser confusos para las personas que encuentran esta respuesta.
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <sstream>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libswscale/swscale.h>
}
int main(int argc, char** argv) {
// Open the initial context variables that are needed
SwsContext *img_convert_ctx;
AVFormatContext* format_ctx = avformat_alloc_context();
AVCodecContext* codec_ctx = NULL;
int video_stream_index;
// Register everything
av_register_all();
avformat_network_init();
//open RTSP
if (avformat_open_input(&format_ctx, "rtsp://134.169.178.187:8554/h264.3gp",
NULL, NULL) != 0) {
return EXIT_FAILURE;
}
if (avformat_find_stream_info(format_ctx, NULL) < 0) {
return EXIT_FAILURE;
}
//search video stream
for (int i = 0; i < format_ctx->nb_streams; i++) {
if (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
video_stream_index = i;
}
AVPacket packet;
av_init_packet(&packet);
//open output file
AVFormatContext* output_ctx = avformat_alloc_context();
AVStream* stream = NULL;
int cnt = 0;
//start reading packets from stream and write them to file
av_read_play(format_ctx); //play RTSP
// Get the codec
AVCodec *codec = NULL;
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
exit(1);
}
// Add this to allocate the context by codec
codec_ctx = avcodec_alloc_context3(codec);
avcodec_get_context_defaults3(codec_ctx, codec);
avcodec_copy_context(codec_ctx, format_ctx->streams[video_stream_index]->codec);
std::ofstream output_file;
if (avcodec_open2(codec_ctx, codec, NULL) < 0)
exit(1);
img_convert_ctx = sws_getContext(codec_ctx->width, codec_ctx->height,
codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_RGB24,
SWS_BICUBIC, NULL, NULL, NULL);
int size = avpicture_get_size(AV_PIX_FMT_YUV420P, codec_ctx->width,
codec_ctx->height);
uint8_t* picture_buffer = (uint8_t*) (av_malloc(size));
AVFrame* picture = av_frame_alloc();
AVFrame* picture_rgb = av_frame_alloc();
int size2 = avpicture_get_size(AV_PIX_FMT_RGB24, codec_ctx->width,
codec_ctx->height);
uint8_t* picture_buffer_2 = (uint8_t*) (av_malloc(size2));
avpicture_fill((AVPicture *) picture, picture_buffer, AV_PIX_FMT_YUV420P,
codec_ctx->width, codec_ctx->height);
avpicture_fill((AVPicture *) picture_rgb, picture_buffer_2, AV_PIX_FMT_RGB24,
codec_ctx->width, codec_ctx->height);
while (av_read_frame(format_ctx, &packet) >= 0 && cnt < 1000) { //read ~ 1000 frames
std::cout << "1 Frame: " << cnt << std::endl;
if (packet.stream_index == video_stream_index) { //packet is video
std::cout << "2 Is Video" << std::endl;
if (stream == NULL) { //create stream in file
std::cout << "3 create stream" << std::endl;
stream = avformat_new_stream(output_ctx,
format_ctx->streams[video_stream_index]->codec->codec);
avcodec_copy_context(stream->codec,
format_ctx->streams[video_stream_index]->codec);
stream->sample_aspect_ratio =
format_ctx->streams[video_stream_index]->codec->sample_aspect_ratio;
}
int check = 0;
packet.stream_index = stream->id;
std::cout << "4 decoding" << std::endl;
int result = avcodec_decode_video2(codec_ctx, picture, &check, &packet);
std::cout << "Bytes decoded " << result << " check " << check
<< std::endl;
if (cnt > 100) //cnt < 0)
{
sws_scale(img_convert_ctx, picture->data, picture->linesize, 0,
codec_ctx->height, picture_rgb->data, picture_rgb->linesize);
std::stringstream file_name;
file_name << "test" << cnt << ".ppm";
output_file.open(file_name.str().c_str());
output_file << "P3 " << codec_ctx->width << " " << codec_ctx->height
<< " 255/n";
for (int y = 0; y < codec_ctx->height; y++) {
for (int x = 0; x < codec_ctx->width * 3; x++)
output_file
<< (int) (picture_rgb->data[0]
+ y * picture_rgb->linesize[0])[x] << " ";
}
output_file.close();
}
cnt++;
}
av_free_packet(&packet);
av_init_packet(&packet);
}
av_free(picture);
av_free(picture_rgb);
av_free(picture_buffer);
av_free(picture_buffer_2);
av_read_pause(format_ctx);
avio_close(output_ctx->pb);
avformat_free_context(output_ctx);
return (EXIT_SUCCESS);
}
Puede ser compilado por algo al efecto de:
g++ -w my_streamer.cpp -o my_streamer $(pkg-config --cflags --libs libavformat libswscale)
-w
la -w
ya que hay muchas advertencias de desaprobación para estas funciones en la biblioteca. Necesitará tener pkg-config, así como las bibliotecas libavformat y libswscale instaladas.
Para las secuencias de rtsp, lo siguiente funciona para mí (después de recibir los marcos, guardo el resultado en un archivo de ppm):
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <sstream>
extern "C"
{
#include <avcodec.h>
#include <avformat.h>
#include <avio.h>
#include <swscale.h>
}
void log_callback(void *ptr, int level, const char *fmt, va_list vargs)
{
static char message[8192];
const char *module = NULL;
if (ptr)
{
AVClass *avc = *(AVClass**) ptr;
module = avc->item_name(ptr);
}
vsnprintf_s(message, sizeof(message), fmt, vargs);
std::cout << "LOG: " << message << std::endl;
}
int main(int argc, char** argv) {
SwsContext *img_convert_ctx;
AVFormatContext* context = avformat_alloc_context();
AVCodecContext* ccontext = avcodec_alloc_context();
int video_stream_index;
av_register_all();
avformat_network_init();
//av_log_set_callback(&log_callback);
//open rtsp
if(avformat_open_input(&context, "rtsp://134.169.178.187:8554/h264.3gp",NULL,NULL) != 0){
return EXIT_FAILURE;
}
if(avformat_find_stream_info(context,NULL) < 0){
return EXIT_FAILURE;
}
//search video stream
for(int i =0;i<context->nb_streams;i++){
if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
video_stream_index = i;
}
AVPacket packet;
av_init_packet(&packet);
//open output file
//AVOutputFormat* fmt = av_guess_format(NULL,"test2.mp4",NULL);
AVFormatContext* oc = avformat_alloc_context();
//oc->oformat = fmt;
//avio_open2(&oc->pb, "test.mp4", AVIO_FLAG_WRITE,NULL,NULL);
AVStream* stream=NULL;
int cnt = 0;
//start reading packets from stream and write them to file
av_read_play(context);//play RTSP
AVCodec *codec = NULL;
codec = avcodec_find_decoder(CODEC_ID_H264);
if (!codec) exit(1);
avcodec_get_context_defaults3(ccontext, codec);
avcodec_copy_context(ccontext,context->streams[video_stream_index]->codec);
std::ofstream myfile;
if (avcodec_open(ccontext, codec) < 0) exit(1);
img_convert_ctx = sws_getContext(ccontext->width, ccontext->height, ccontext->pix_fmt, ccontext->width, ccontext->height,
PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
int size = avpicture_get_size(PIX_FMT_YUV420P, ccontext->width, ccontext->height);
uint8_t* picture_buf = (uint8_t*)(av_malloc(size));
AVFrame* pic = avcodec_alloc_frame();
AVFrame* picrgb = avcodec_alloc_frame();
int size2 = avpicture_get_size(PIX_FMT_RGB24, ccontext->width, ccontext->height);
uint8_t* picture_buf2 = (uint8_t*)(av_malloc(size2));
avpicture_fill((AVPicture *) pic, picture_buf, PIX_FMT_YUV420P, ccontext->width, ccontext->height);
avpicture_fill((AVPicture *) picrgb, picture_buf2, PIX_FMT_RGB24, ccontext->width, ccontext->height);
while(av_read_frame(context,&packet)>=0 && cnt <1000)
{//read 100 frames
std::cout << "1 Frame: " << cnt << std::endl;
if(packet.stream_index == video_stream_index){//packet is video
std::cout << "2 Is Video" << std::endl;
if(stream == NULL)
{//create stream in file
std::cout << "3 create stream" << std::endl;
stream = avformat_new_stream(oc,context->streams[video_stream_index]->codec->codec);
avcodec_copy_context(stream->codec,context->streams[video_stream_index]->codec);
stream->sample_aspect_ratio = context->streams[video_stream_index]->codec->sample_aspect_ratio;
}
int check = 0;
packet.stream_index = stream->id;
std::cout << "4 decoding" << std::endl;
int result = avcodec_decode_video2(ccontext, pic, &check, &packet);
std::cout << "Bytes decoded " << result << " check " << check << std::endl;
if(cnt > 100)//cnt < 0)
{
sws_scale(img_convert_ctx, pic->data, pic->linesize, 0, ccontext->height, picrgb->data, picrgb->linesize);
std::stringstream name;
name << "test" << cnt << ".ppm";
myfile.open(name.str());
myfile << "P3 " << ccontext->width << " " << ccontext->height << " 255/n";
for(int y = 0; y < ccontext->height; y++)
{
for(int x = 0; x < ccontext->width * 3; x++)
myfile << (int)(picrgb->data[0] + y * picrgb->linesize[0])[x] << " ";
}
myfile.close();
}
cnt++;
}
av_free_packet(&packet);
av_init_packet(&packet);
}
av_free(pic);
av_free(picrgb);
av_free(picture_buf);
av_free(picture_buf2);
av_read_pause(context);
avio_close(oc->pb);
avformat_free_context(oc);
return (EXIT_SUCCESS);
}
Revise su archivo ./configure. Puede ser su compilación para armlinux
no para el x86
. es por eso que está obteniendo una referencia indefinida a ''avcodec_register_all''
Aquí está la solución:
$ cd ffmpeg
$export LD_RUN_PATH=/usr/local/lib:/usr/lib:/lib
$ ./configure
$ make && make install
Y después de eso compila tu aplicación.