I am working on a screen streaming program, till now, I can capture and encode my screen into a video perfectly.
However, whenever I try to stream it over LAN, and play using Mplayer, it is able to run in simple desktop (text document, etc...) but when I try to play video the frame stated to corrupted until I quit the video (this video cause the most corrupted frame https://www.youtube.com/watch?v=1La4QzGeaaQ&t=230s)
I am not really sure why when streaming, I got corrupted frame with the youtube video but when save it to a file (change URL from udp://192.168.1.xxx:YYYY to file name C:\Test.ts) there were no corrupted frame at all.
My Mplayer log:
`V: 0.0 1563/1563 51% 43% 0.0% 0 0
[h264 # 0000000001d04940]Invalid NAL unit 0, skipping.
[h264 # 0000000001d04940]error while decoding MB 23 51, bytestream -64
[h264 # 0000000001d04940]concealing 2066 DC, 2066 AC, 2066 MV errors in I frame
V: 0.0 1564/1564 51% 43% 0.0% 0 0
[h264 # 0000000001d04940]concealing 7598 DC, 7598 AC, 7598 MV errors in P frame
V: 0.0 1652/1652 50% 43% 0.0% 0 0
[h264 # 0000000001d04940]Invalid NAL unit 0, skipping.
[h264 # 0000000001d04940]error while decoding MB 26 49, bytestream -55
[h264 # 0000000001d04940]concealing 2303 DC, 2303 AC, 2303 MV errors in I frame
V: 0.0 1653/1653 50% 43% 0.0% 0 0
[h264 # 0000000001d04940]concealing 7727 DC, 7727 AC, 7727 MV errors in P frame
V: 0.0 1741/1741 49% 43% 0.0% 0 0
[h264 # 0000000001d04940]Invalid NAL unit 0, skipping.
[h264 # 0000000001d04940]error while decoding MB 65 62, bytestream -57
[h264 # 0000000001d04940]concealing 704 DC, 704 AC, 704 MV errors in I frame
V: 0.0 1742/1742 49% 43% 0.0% 0 0`
Stream initialize code
static void Stream_ini(const char *Url, AVFormatContext *&ofmt_ctx, AVCodec *codec, AVCodecContext *c, AVStream *&out_stream)
{
int ret;
avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", Url);
out_stream = avformat_new_stream(ofmt_ctx, codec)
out_stream->codec = c;
av_dump_format(ofmt_ctx, 0, Url, 1);
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
if (!(ofmt_ctx->flags & AVFMT_NOFILE))
{
ret = avio_open(&ofmt_ctx->pb, Url, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("Could not open output URL '%s'", Url);
return;
}
}
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0)
{
printf("Error occurred when opening output URL\n");
return;
}
}
this code will get the captured screen and send it to encoder:
ScreenCap.load_screen_data(inFrame,timmer) // this will capture screen and return the time and AVFrame for encoding
sws_scale(ctx, inFrame->data, inFrame->linesize, 0,c->height, frame->data, frame->linesize);
frame->pts = (timmer - first_frame_time) / fps;
EncodeToPkT(c, frame, pkt, ofmt_ctx,out_stream);
av_frame_free(&inFrame);
the AVFrame will then be send to encoder using avcodec_send_frame() to get Packet data and av_interleaved_write_frame() to stream it over LAN.
All error check are removed for simplicity
Also, this is my AVCodecContex setting for the encoder:
c->bit_rate = 15000000;
c->width = 1920;
c->height = 1080;
c->time_base = AVRational{ 1, 90 };
c->framerate = AVRational{ 90, 1 };
c->gop_size = 90;
c->max_b_frames = 0;
c->pix_fmt = AV_PIX_FMT_RGB0;
I also notice it only happen when I increase encoder bitrate (15 MBIT) but when lower it down to (10 MBIT) it happen less. And when lower it down to 2 MBIT the corrupted frame not happen anymore however the quality is really bad.
I test the streaming in LAN using: PC -> cable ->laptop, PC->wireless->laptop, PC->Virtual PC, PC only (enter PC IP address in both program and Mplayer), all got the same result.
I also test my DXGICap.load_screen_data function by make it output raw image and there were no corrupted image at all
Does anybody have an idea why.
Thank Nam.
It's not just a case of using UDP, ie. you'll receive packets out-of-order and the decoding will then fail/complain? Try using TCP and see what happens.
Related
I need to record frames in real time. To test this situation, I make pts non-linear (since frames may be lost), thus:
// AVFrame
video_frame->pts = prev_pts + 2;
I use libavformat to write to a file. Parameters AVCodecContext and AVStream:
#define STREAM_FRAME_RATE 25
#define CODEC_PIX_FMT AV_PIX_FMT_YUV420P
#define FRAME_WIDTH 1440
#define FRAME_HEIGHT 900
// AVCodecContext
cc->codec_id = video_codec->id;
cc->bit_rate = 400000;
cc->width = FRAME_WIDTH;
cc->height = FRAME_HEIGHT;
cc->gop_size = 12;
cc->pix_fmt = CODEC_PIX_FMT;
// AVStream
video_stream->time_base = AVRational{ 1, STREAM_FRAME_RATE };
cc->time_base = video_stream->time_base;
cc->framerate = AVRational{ STREAM_FRAME_RATE , 1 };
Write to file:
static 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->pts = av_rescale_q(pkt->pts, *time_base, st->time_base);
pkt->dts = av_rescale_q(pkt->dts, *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_write_frame(fmt_ctx, pkt);
return av_interleaved_write_frame(fmt_ctx, pkt);
}
If you use the avi container, then the information on the number of frames per second is indicated correctly in the file: 25 fps
If you use the mp4 container, then the file information about the number of frames per second is indicated incorrectly: 12.5 fps
Tell me, please, what other settings need to be added?
MP4s do not store framerate, AVIs do.
In MP4s, only timing info for packets is stored. Since your pts expr is video_frame->pts = prev_pts + 2 and stream time base is 1/25, frames are spaced 80ms apart and hence ffmpeg probes the frame rate to be 12.5 fps (correctly).
AVIs do not have per-frame timing. Instead, they write the user-supplied frame rate. Should a packet timing be greater than the pervious frame pts by 1/fps, the muxer will write skip frame(s) which are empty packets, to maintain the frame rate.
I render some images with OpenGL and need to compose them into a video file. Each of the images is a sequence of uint8_t values representing a sRGB color component (image array looks like ...rgbrgbrgb...)
I know very little about video processing and have no experience with ffmpeg libraries at all. I made a little test program using these sources as reference:
https://ffmpeg.org/doxygen/trunk/encode_video_8c-example.html
How to convert RGB from YUV420p for ffmpeg encoder?
The test program is supposed to make a video about growing green vertical stripe. I'm just trying to figure out how to make a video using some source of raw RGB data.
Here is my code:
#include <iostream>
#include <vector>
#include <algorithm>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
static void encode( AVCodecContext* enc_ctx,
AVFrame* frame, AVPacket* pkt,
FILE* outfile )
{
int ret;
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
std::cerr << "Error sending a frame for encoding\n";
return;
}
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during encoding\n");
exit(1);
}
fwrite(pkt->data, 1, pkt->size, outfile);
av_packet_unref(pkt);
}
}
static constexpr int w = 1920, h = 1080;
static constexpr float fps = 20.f, time = 5.f;
static constexpr int nFrames = static_cast<int>(fps * time);
static std::vector<uint8_t> imageRGB(w * h * 3, 0);
static void UpdateImageRGB()
{
static int d = 50;
imageRGB.assign(w * h * 3, 0);
for (int i = 0; i < h; ++i)
for ( int j = std::max(0, w / 2 - d);
j < std::min(w, w / 2 + d);
++j )
{
imageRGB[(w * i + j) * 3 + 0] = 50;
imageRGB[(w * i + j) * 3 + 1] = 200;
imageRGB[(w * i + j) * 3 + 2] = 50;
}
d += 5;
}
int main()
{
int ret = 0;
auto filename = "test.mp4";
auto codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec) {
std::cerr << "Codec \"x.264\" not found\n";
return 1;
}
auto c = avcodec_alloc_context3(codec);
if (!c) {
std::cerr << "Could not allocate video codec context\n";
return 1;
}
auto pkt = av_packet_alloc();
if (!pkt) return 1;
// 1.8 bits / (pixel * frame)
c->bit_rate = static_cast<int64_t>(1.8f * w * h * fps);
/* resolution must be a multiple of two */
c->width = w;
c->height = h;
/* frames per second */
c->time_base = AVRational{ 1, static_cast<int>(fps) };
c->framerate = AVRational{ static_cast<int>(fps), 1 };
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set(c->priv_data, "preset", "slow", 0);
av_opt_set(c->priv_data, "preset", "slow", 0);
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
char str[AV_ERROR_MAX_STRING_SIZE];
std::cerr << "Could not open codec: "
<< av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, ret)
<< "\n";
return 1;
}
FILE * f;
fopen_s(&f, filename, "wb");
if (!f) {
std::cerr << "Could not open " << filename << '\n';
return 1;
}
auto frame = av_frame_alloc();
if (!frame) {
std::cerr << "Could not allocate video frame\n";
return 1;
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
std::cerr << stderr, "Could not allocate the video frame data\n";
return 1;
}
SwsContext* ctx = sws_getContext( w, h, AV_PIX_FMT_RGB24,
w, h, AV_PIX_FMT_YUV420P,
0, 0, 0, 0 );
for (int i = 0; i < nFrames; i++)
{
ret = av_frame_make_writable(frame);
UpdateImageRGB();
static const uint8_t* rgbData[1] = { &imageRGB[0] };
static constexpr int rgbLinesize[1] = { 3 * w };
sws_scale( ctx, rgbData, rgbLinesize, 0, h,
frame->data, frame->linesize );
frame->pts = i;
/* encode the image */
encode(c, frame, pkt, f);
}
encode(c, NULL, pkt, f);
fclose(f);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
return 0;
}
The program generates 33.9k video file with further console output:
[libx264 # 0000020c18681800] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 # 0000020c18681800] profile High, level 5.0, 4:2:0, 8-bit
[libx264 # 0000020c18681800] frame I:11 Avg QP: 0.00 size: 639
[libx264 # 0000020c18681800] frame P:74 Avg QP: 0.32 size: 174
[libx264 # 0000020c18681800] frame B:15 Avg QP: 2.26 size: 990
[libx264 # 0000020c18681800] consecutive B-frames: 70.0% 30.0%
[libx264 # 0000020c18681800] mb I I16..4: 100.0% 0.0% 0.0%
[libx264 # 0000020c18681800] mb P I16..4: 0.6% 0.0% 0.0% P16..4: 2.1% 0.0% 0.0% 0.0% 0.0% skip:97.3%
[libx264 # 0000020c18681800] mb B I16..4: 0.1% 0.0% 0.0% B16..8: 0.6% 0.0% 0.0% direct: 0.6% skip:98.7% L0:39.8% L1:60.2% BI: 0.0%
[libx264 # 0000020c18681800] final ratefactor: -46.47
[libx264 # 0000020c18681800] 8x8 transform intra:0.0%
[libx264 # 0000020c18681800] direct mvs spatial:0.0% temporal:100.0%
[libx264 # 0000020c18681800] coded y,uvDC,uvAC intra: 0.0% 0.1% 0.1% inter: 0.0% 0.1% 0.1%
[libx264 # 0000020c18681800] i16 v,h,dc,p: 99% 0% 1% 0%
[libx264 # 0000020c18681800] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 0% 0% 100% 0% 0% 0% 0% 0% 0%
[libx264 # 0000020c18681800] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 46% 0% 54% 0% 0% 0% 0% 0% 0%
[libx264 # 0000020c18681800] i8c dc,h,v,p: 96% 1% 3% 0%
[libx264 # 0000020c18681800] Weighted P-Frames: Y:0.0% UV:0.0%
[libx264 # 0000020c18681800] ref P L0: 70.2% 0.0% 29.8% 0.0% 0.0%
[libx264 # 0000020c18681800] kb/s:55.61
"Media Player Classic" on Windows plays this video but the time slider doesn't move, and the video cannot be fast-forwarded to some frame
VLC cannot play the video at all. It launches, shows me VLC logo, and time slider (which is unusually big) jumps from left to right, not responding to my clicks
If I set time = 0.05 to make a video of only 1 frame, I cannot play it even with "Media Player Classic". I want to make an algorithm to convert the arbitrary number of raw RGB images into the video files, even if there's only one image, and with arbitrary image size (that is, width and height may be odd).
As I said, I don't really understand what am I doing. There are low-level codec settings in lines 83-84. Are they all right?
Do I have to manually set a bit rate (line 75)? Shouldn't it be calculated automatically by the codec?
Hey I solved this by referencing the method used here. I don't know if you can apply this in your code but I recommend you to check this out:
https://superuser.com/questions/469273/ffmpeg-convert-rgb-images-to-video
The final .mp4 file aways called Container.
And a Container has its owner format.
You write encoded data to a .mp4 file directly is not right.
You need to read the muxing example code from ffmpeg source.
muxing.c file is in ffmpeg source directory:doc/examples/muxing.c
i have some ffmpeg code in c++ that generates a RTMP stream from H264 NALU and audio samples encoded in AAC. I'am using NGINX to take the RTMP stream and forwards to clients and it is working fine. My issue is that when i use NGINX to convert the RTMP stream to HLS, there is no HLS chunks and playlist generated. I use ffmpeg to copy the RTMP stream and generate a new stream to NGINX, the HLS conversion works.
Here is what i get when i do the stream copy using FFMPEG :
Input #0, flv, from 'rtmp://127.0.0.1/live/beam_0'
Metadata:
Server : NGINX RTMP (github.com/sergey-dryabzhinsky/nginx-rtmp-module)
displayWidth : 1920
displayHeight : 1080
fps : 30
profile :
level :
Duration: 00:00:00.00, start: 5.019000, bitrate: N/A
Stream #0:0: Audio: aac, 44100 Hz, mono, fltp, 128 kb/s
Stream #0:1: Video: h264 (High), 1 reference frame, yuv420p(progressive, left), 1920x1080 (1920x1088), 8000 kb/s, 30 fps, 30.30 tbr, 1k tbn, 60 tbc
Output #0, flv, to 'rtmp://localhost/live/copy_stream':
Metadata:
Server : NGINX RTMP (github.com/sergey-dryabzhinsky/nginx-rtmp-module)
displayWidth : 1920
displayHeight : 1080
fps : 30
profile :
level :
encoder : Lavf57.83.100
Stream #0:0: Video: h264 (High), 1 reference frame ([7][0][0][0] / 0x0007), yuv420p(progressive, left), 1920x1080 (0x0), q=2-31, 8000 kb/s, 30 fps, 30.30 tbr, 1k tbn, 1k tbc
Stream #0:1: Audio: aac ([10][0][0][0] / 0x000A), 44100 Hz, mono, fltp, 128 kb/s
There are not much differences between the two streams, so i don't really get what is going wrong and what i should change in my C++ code. One very weird issue i see is that my audio stream is 48kHz when i publish it, but here it is recognized as 44100Hz :
Output #0, flv, to 'rtmp://127.0.0.1/live/beam_0':
Stream #0:0, 0, 1/1000: Video: h264 (libx264), 1 reference frame, yuv420p, 1920x1080, 0/1, q=-1--1, 8000 kb/s, 30 fps, 1k tbn, 1k tbc
Stream #0:1, 0, 1/1000: Audio: aac, 48000 Hz, 1 channels, fltp, 128 kb/s
UPDATE 1 :
The output context is created using the following code :
pOutputFormatContext->oformat = av_guess_format("flv", url.toStdString().c_str(), nullptr);
memcpy(pOutputFormatContext->filename, url.toStdString().c_str(), url.length());
avio_open(&pOutputFormatContext->pb, url.toStdString().c_str(), AVIO_FLAG_WRITE));
pOutputFormatContext->oformat->video_codec = AV_CODEC_ID_H264 ;
pOutputFormatContext->oformat->audio_codec = AV_CODEC_ID_AAC ;
The audio stream is created with :
AVDictionary *opts = nullptr;
//pAudioCodec = avcodec_find_encoder(AV_CODEC_ID_VORBIS);
pAudioCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
pAudioCodecContext = avcodec_alloc_context3(pAudioCodec);
pAudioCodecContext->thread_count = 1;
pAudioFrame = av_frame_alloc();
av_dict_set(&opts, "strict", "experimental", 0);
pAudioCodecContext->bit_rate = 128000;
pAudioCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
pAudioCodecContext->sample_rate = static_cast<int>(sample_rate) ;
pAudioCodecContext->channels = nb_channels ;
pAudioCodecContext->time_base.num = 1;
pAudioCodecContext->time_base.den = 1000 ;
//pAudioCodecContext->time_base.den = static_cast<int>(sample_rate) ;
pAudioCodecContext->codec_type = AVMEDIA_TYPE_AUDIO;
avcodec_open2(pAudioCodecContext, pAudioCodec, &opts);
pAudioFrame->nb_samples = pAudioCodecContext->frame_size;
pAudioFrame->format = pAudioCodecContext->sample_fmt;
pAudioFrame->channel_layout = pAudioCodecContext->channel_layout;
mAudioSamplesBufferSize = av_samples_get_buffer_size(nullptr, pAudioCodecContext->channels, pAudioCodecContext->frame_size, pAudioCodecContext->sample_fmt, 0);
avcodec_fill_audio_frame(pAudioFrame, pAudioCodecContext->channels, pAudioCodecContext->sample_fmt, (const uint8_t*) pAudioSamples, mAudioSamplesBufferSize, 0);
if( pOutputFormatContext->oformat->flags & AVFMT_GLOBALHEADER )
pAudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
pAudioStream = avformat_new_stream(pOutputFormatContext, 0);
pAudioStream->codec = pAudioCodecContext ;
pAudioStream->id = pOutputFormatContext->nb_streams-1;;
pAudioStream->time_base.den = pAudioStream->pts.den = pAudioCodecContext->time_base.den;
pAudioStream->time_base.num = pAudioStream->pts.num = pAudioCodecContext->time_base.num;
mAudioPacketTs = 0 ;
The video stream is created with :
pVideoCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
pVideoCodecContext = avcodec_alloc_context3(pVideoCodec);
pVideoCodecContext->codec_type = AVMEDIA_TYPE_VIDEO ;
pVideoCodecContext->thread_count = 1 ;
pVideoCodecContext->width = width;
pVideoCodecContext->height = height;
pVideoCodecContext->bit_rate = 8000000 ;
pVideoCodecContext->time_base.den = 1000 ;
pVideoCodecContext->time_base.num = 1 ;
pVideoCodecContext->gop_size = 10;
pVideoCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
pVideoCodecContext->flags = 0x0007 ;
pVideoCodecContext->extradata_size = sizeof(extra_data_buffer);
pVideoCodecContext->extradata = (uint8_t *) av_malloc ( sizeof(extra_data_buffer) );
memcpy ( pVideoCodecContext->extradata, extra_data_buffer, sizeof(extra_data_buffer));
avcodec_open2(pVideoCodecContext,pVideoCodec,0);
pVideoFrame = av_frame_alloc();
AVDictionary *opts = nullptr;
av_dict_set(&opts, "strict", "experimental", 0);
memcpy(pOutputFormatContext->filename, this->mStreamUrl.toStdString().c_str(), this->mStreamUrl.length());
pOutputFormatContext->oformat->video_codec = AV_CODEC_ID_H264 ;
if( pOutputFormatContext->oformat->flags & AVFMT_GLOBALHEADER )
pVideoCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
pVideoStream = avformat_new_stream(pOutputFormatContext, pVideoCodec);
//This following section is because AVFormat complains about parameters being passed throught the context and not CodecPar
pVideoStream->codec = pVideoCodecContext ;
pVideoStream->id = pOutputFormatContext->nb_streams-1;
pVideoStream->time_base.den = pVideoStream->pts.den = pVideoCodecContext->time_base.den;
pVideoStream->time_base.num = pVideoStream->pts.num = pVideoCodecContext->time_base.num;
pVideoStream->avg_frame_rate.num = fps ;
pVideoStream->avg_frame_rate.den = 1 ;
pVideoStream->codec->gop_size = 10 ;
mVideoPacketTs = 0 ;
Then each video packet and audio packet is pushed with correct scaled pts/dts. I have corrected the 48kHz issue. It was because i was configuring the stream through the codec context and the through the codec parameters (because of waarning at runtime).
This RTMP stream still does not work for HLS conversion by NGINX, but if i just use FFMPEG to take the RTMP stream from NGINX and re-publish it with copy codec then it works.
Good day.
For brevity, the code omits error handling and memory management.
I want to capture h264 video stream and pack it to mp4 container without changes. Since i don't control the source of stream, i can not make assumptions about stream structure. In this way i must probe input stream.
AVProbeData probeData;
probeData.buf_size = s->BodySize();
probeData.buf = s->GetBody();
probeData.filename = "";
AVInputFormat* inFormat = av_probe_input_format(&probeData, 1);
This code correctly defines h264 stream.
Next, i create input format context,
unsigned char* avio_input_buffer = reinterpret_cast<unsigned char*> (av_malloc(AVIO_BUFFER_SIZE));
AVIOContext* avio_input_ctx = avio_alloc_context(avio_input_buffer, AVIO_BUFFER_SIZE,
0, this, &read_packet, NULL, NULL);
AVFormatContext* ifmt_ctx = avformat_alloc_context();
ifmt_ctx->pb = avio_input_ctx;
int ret = avformat_open_input(&ifmt_ctx, NULL, inFormat, NULL);
set image size,
ifmt_ctx->streams[0]->codec->width = ifmt_ctx->streams[0]->codec->coded_width = width;
ifmt_ctx->streams[0]->codec->height = ifmt_ctx->streams[0]->codec->coded_height = height;
create output format context,
unsigned char* avio_output_buffer = reinterpret_cast<unsigned char*>(av_malloc(AVIO_BUFFER_SIZE));
AVIOContext* avio_output_ctx = avio_alloc_context(avio_output_buffer, AVIO_BUFFER_SIZE,
1, this, NULL, &write_packet, NULL);
AVFormatContext* ofmt_ctx = nullptr;
avformat_alloc_output_context2(&ofmt_ctx, NULL, "mp4", NULL);
ofmt_ctx->pb = avio_output_ctx;
AVDictionary* dict = nullptr;
av_dict_set(&dict, "movflags", "faststart", 0);
av_dict_set(&dict, "movflags", "frag_keyframe+empty_moov", 0);
AVStream* outVideoStream = avformat_new_stream(ofmt_ctx, nullptr);
avcodec_copy_context(outVideoStream->codec, ifmt_ctx->streams[0]->codec);
ret = avformat_write_header(ofmt_ctx, &dict);
Initialization is done. Further there is a shifting packets from h264 stream to mp4 container. I dont calculate pts and dts, because source packet has AV_NOPTS_VALUE in them.
AVPacket pkt;
while (...)
{
ret = av_read_frame(ifmt_ctx, &pkt);
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
av_free_packet(&pkt);
}
Further i write trailer and free allocated memory. That is all. Code works and i got playable mp4 file.
Now the problem: the stream characteristics of the resulting file is not completely consisent with the characteristics of the source stream. In particular, fps and bitrate is higher than it should be.
As sample, below is output ffplay.exe for source stream
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'd:/movies/source.mp4':0/0
Metadata:
major_brand : isom
minor_version : 1
compatible_brands: isom
creation_time : 2014-04-14T13:03:54.000000Z
Duration: 00:00:58.08, start: 0.000000, bitrate: 12130 kb/s
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661),yuv420p, 1920x1080, 12129 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
Switch subtitle stream from #-1 to #-1 vq= 1428KB sq= 0B f=0/0
Seek to 49% ( 0:00:28) of total duration ( 0:00:58) B f=0/0
30.32 M-V: -0.030 fd= 87 aq= 0KB vq= 1360KB sq= 0B f=0/0
and for resulting stream (contains part of source stream)
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'd:/movies/target.mp4':f=0/0
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1iso6mp41
encoder : Lavf57.56.101
Duration: 00:00:11.64, start: 0.000000, bitrate: 18686 kb/s
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 1920x1080, 18683 kb/s, 38.57 fps, 40 tbr, 90k tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
Switch subtitle stream from #-1 to #-1 vq= 2309KB sq= 0B f=0/0
5.70 M-V: 0.040 fd= 127 aq= 0KB vq= 2562KB sq= 0B f=0/0
So there is a question, what i missed when copying stream? I will be grateful for any help.
Best regards
I dont calculate pts and dts This is your problem. frame rate and bit rate are both ratios where time is the denominator. But not writing pts/dts you end up with a video shorter than you want. h.264 does not timestamp every frame. that is the containers job. You must make up time stamps from the known frame rate, or another value.
Insights to encode/decode video with ffmpeg h264 (lossless)
So I got something working on the encoding part, encode an avi in 264 however VLC wont play it, however Totem will.
Decoding the same file proves troublesome. (I want the exact same data/frame going in as going out), I get these ;
saving frame 5
Video decoding
[h264 # 0x1d19880] decode_slice_header error
frame :6
saving frame 6
Video decoding
[h264 # 0x1d19880] error while decoding MB 15 7, bytestream -27
[h264 # 0x1d19880] concealing 194 DC, 194 AC, 194 MV errors in I frame
frame :7
saving frame 7
Video decoding
[h264 # 0x1d19880] decode_slice_header error
and ultimatly this
[H264 Decoder # 0x7f1320766040] frame :11
Broken frame packetizing
[h264 # 0x1d19880] SPS changed in the middle of the frame
[h264 # 0x1d19880] decode_slice_header error
[h264 # 0x1d19880] no frame!
Error while decoding frame 11
GAME OVER.
Now I suspect that I have to go back to 1. the encoding part, there is problary a good reason VLC wont play it!
I encode like this.
void encode(char *Y,char *U,char *V){
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
fflush(stdout);
frame->data[0] = (uint8_t*)Y;
frame->data[1] = (uint8_t*)U;
frame->data[2] = (uint8_t*)V;
frame->pts = ++i;
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit (EXIT_FAILURE);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
And the codec is setup like this:
AVCodecID dasd = AV_CODEC_ID_H264;
codec = avcodec_find_encoder(dasd);
c = avcodec_alloc_context3(codec);
c->bit_rate = 400000;
c->width = 320;
c->height = 240;
c->time_base= (AVRational){1,25};
c->gop_size = 10;
c->max_b_frames=1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set(c->priv_data, "preset", "slow", 0);
avcodec_open2(c, codec, NULL);
Since I am going for lossless i am not dealing with delayed frames(is this a correct assumption?)
I may not actually be encoding lossless, it seems like I may have to go with something like
AVDictionary *param;
av_dict_set(¶m, "qp", "0", 0);
And then open...
So I guess me questions is these :
What are the correct codec params for lossless encoding (and advice if h264 is a terrible idea in this regard).
Do I need to handle delayed frames when going for lossless?
Why is VLC mad at me?
Thanks.
To achieve lossless: av_dict_set(¶m, "crf", "0", 0);
Delayed frames (B-frames) has nothing to do with lossless. If you need low-delay, then don't use B-frames.
Some thing is seriously wrong in your encoding. The error "MV errors in I frame" is odd one here, there shouldn't be any MVs in I-frame. It seems the header parsing it-self gone wrong. Please share the bit-stream & more details for VLC failure
You're writing raw annexb frames into a file without any container wrapping. Use a container like mp4 or matroska and VLC should be happy.