Skip to content

Commit dbab188

Browse files
first stepo transmuxing
1 parent 2969dbe commit dbab188

File tree

5 files changed

+368
-0
lines changed

5 files changed

+368
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
build/*
33
small_bunny_1080p_60fps.mp4
44
bunny_1080p_60fps.mp4
5+
bunny_1s_gop.mp4

3_transcoding.c

+272
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
#include <libavcodec/avcodec.h>
2+
#include <libavformat/avformat.h>
3+
#include <libavutil/timestamp.h>
4+
#include <stdio.h>
5+
#include <stdarg.h>
6+
#include <stdlib.h>
7+
#include <libavutil/opt.h>
8+
#include <string.h>
9+
#include <inttypes.h>
10+
#include "video_debugging.h"
11+
12+
typedef struct StreamingParams {
13+
char copy_video;
14+
char copy_audio;
15+
} StreamingParams;
16+
17+
typedef struct StreamingContext {
18+
AVFormatContext *avfc;
19+
AVCodec *video_avc;
20+
AVCodec *audio_avc;
21+
AVStream *video_avs;
22+
AVStream *audio_avs;
23+
AVCodecContext *video_avcc;
24+
AVCodecContext *audio_avcc;
25+
int video_index;
26+
int audio_index;
27+
char *filename;
28+
} StreamingContext;
29+
30+
int fill_stream_info(AVStream *avs, AVCodec **avc, AVCodecContext **avcc) {
31+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga19a0ca553277f019dd5b0fec6e1f9dca
32+
// Find a registered decoder with a matching codec ID.
33+
*avc = avcodec_find_decoder(avs->codecpar->codec_id);
34+
if (!*avc) {logging("failed to find the codec"); return -1;}
35+
36+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#gae80afec6f26df6607eaacf39b561c315
37+
// Allocate an AVCodecContext and set its fields to default values.
38+
*avcc = avcodec_alloc_context3(*avc);
39+
if (!*avcc) {logging("failed to alloc memory for codec context"); return -1;}
40+
41+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#gac7b282f51540ca7a99416a3ba6ee0d16
42+
// Fill the codec context based on the values from the supplied codec parameters.
43+
if (avcodec_parameters_to_context(*avcc, avs->codecpar) < 0) {logging("failed to fill codec context"); return -1;}
44+
45+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d
46+
// Initialize the AVCodecContext to use the given AVCodec.
47+
if (avcodec_open2(*avcc, *avc, NULL) < 0) {logging("failed to open codec"); return -1;}
48+
return 0;
49+
}
50+
51+
int open_media(const char *in_filename, AVFormatContext **avfc) {
52+
//https://www.ffmpeg.org/doxygen/trunk/group__lavf__core.html#gac7a91abf2f59648d995894711f070f62
53+
// Allocate an AVFormatContext.
54+
*avfc = avformat_alloc_context();
55+
if (!*avfc) {logging("failed to alloc memory for format"); return -1;}
56+
57+
//https://www.ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#gaff468dcc45289542f4c30d311bc2a201
58+
// Open an input stream and read the header.
59+
if (avformat_open_input(avfc, in_filename, NULL, NULL) != 0) {logging("failed to open input file %s", in_filename); return -1;}
60+
61+
//https://www.ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#gad42172e27cddafb81096939783b157bb
62+
// Read packets of a media file to get stream information.
63+
if (avformat_find_stream_info(*avfc, NULL) < 0) {logging("failed to get stream info"); return -1;}
64+
return 0;
65+
}
66+
67+
int prepare_decoder(StreamingContext *sc) {
68+
// iterating through all the input streams (audio, video, subtitles and etc)
69+
// if we have multiples streams of audio and video we're going to take the last
70+
for (int i = 0; i < sc->avfc->nb_streams; i++) {
71+
if (sc->avfc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
72+
// keeping a pointer to the video av stream
73+
sc->video_avs = sc->avfc->streams[i];
74+
sc->video_index = i;
75+
76+
if (fill_stream_info(sc->video_avs, &sc->video_avc, &sc->video_avcc)) {return -1;}
77+
print_timing("Video timming info from the decoder preparation", sc->avfc, sc->video_avcc, sc->video_avs);
78+
} else if (sc->avfc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
79+
// keeping a pointer to the audio av stream
80+
sc->audio_avs = sc->avfc->streams[i];
81+
sc->audio_index = i;
82+
83+
if (fill_stream_info(sc->audio_avs, &sc->audio_avc, &sc->audio_avcc)) {return -1;}
84+
print_timing("Audio timming info from the decoder preparation", sc->avfc, sc->audio_avcc, sc->audio_avs);
85+
} else {
86+
logging("skipping streams other than audio and video");
87+
}
88+
}
89+
90+
return 0;
91+
}
92+
93+
int prepare_encoder(StreamingContext *sc, AVCodecParameters *decoder_par, AVRational input_framerate) {
94+
/*
95+
* Prepping the encoder - video stream to be transcoded
96+
*/
97+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__encoding.html#gaa614ffc38511c104bdff4a3afa086d37
98+
// Find a registered encoder with the specified name.
99+
sc->video_avc = avcodec_find_encoder_by_name("libx264");
100+
if (!sc->video_avc) {logging("could not find the proper codec"); return -1;}
101+
102+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#gae80afec6f26df6607eaacf39b561c315
103+
// Allocate an AVCodecContext and set its fields to default values.
104+
sc->video_avcc = avcodec_alloc_context3(sc->video_avc);
105+
if (!sc->video_avcc) {logging("could not allocated memory for codec context"); return -1;}
106+
107+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#gac7b282f51540ca7a99416a3ba6ee0d16
108+
// Fill the codec context based on the values from the supplied codec parameters.
109+
if(avcodec_parameters_to_context(sc->video_avcc, decoder_par) < 0){logging("failed to copy codec parameters from the decoder"); return -1;}
110+
print_timing("Video encoder 1", sc->avfc, sc->video_avcc, sc->video_avs);
111+
112+
av_opt_set(sc->video_avcc->priv_data, "preset", "slow", 0);
113+
//av_opt_set(sc->video_avcc->priv_data, "x264opts", "keyint=60:min-keyint=60:scenecut=-1", 0);
114+
115+
AVDictionary *encoder_private_options = NULL;
116+
//av_dict_set(&encoder_private_options , "b", "2.0M", 0);
117+
118+
sc->video_avcc->time_base = input_framerate;
119+
120+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d
121+
// Initialize the AVCodecContext to use the given AVCodec. (does it resets the avcc?)
122+
if (avcodec_open2(sc->video_avcc, sc->video_avc, &encoder_private_options) < 0) {logging("could not open the codec"); return -1;}
123+
print_timing("Video encoder 2", sc->avfc, sc->video_avcc, sc->video_avs);
124+
125+
sc->video_avs = avformat_new_stream(sc->avfc, NULL);
126+
//sc->video_avs->time_base = (AVRational){1, input_framerate};
127+
128+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga6d02e640ccc12c783841ce51d09b9fa7
129+
// Any allocated fields in dst are freed and replaced with newly allocated duplicates of the corresponding fields in src.
130+
avcodec_parameters_copy(sc->video_avs->codecpar, decoder_par);
131+
print_timing("Video timming info from the encoder preparation", sc->avfc, sc->video_avcc, sc->video_avs);
132+
print_timing("Video encoder 3", sc->avfc, sc->video_avcc, sc->video_avs);
133+
134+
return 0;
135+
}
136+
137+
int prepare_copy(AVFormatContext *avfc, AVStream **avs, AVCodecParameters *decoder_par) {
138+
*avs = avformat_new_stream(avfc, NULL);
139+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga6d02e640ccc12c783841ce51d09b9fa7
140+
// Any allocated fields in dst are freed and replaced with newly allocated duplicates of the corresponding fields in src.
141+
avcodec_parameters_copy((*avs)->codecpar, decoder_par);
142+
return 0;
143+
}
144+
145+
int remux(AVPacket **pkt, AVFormatContext **avfc, AVRational decoder_tb, AVRational encoder_tb) {
146+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__packet.html#gae5c86e4d93f6e7aa62ef2c60763ea67e
147+
// Convert valid timing fields (timestamps / durations) in a packet from one timebase to another.
148+
av_packet_rescale_ts(*pkt, decoder_tb, encoder_tb);
149+
150+
//https://www.ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga37352ed2c63493c38219d935e71db6c1
151+
// Write a packet to an output media file ensuring correct interleaving
152+
if (av_interleaved_write_frame(*avfc, *pkt) < 0) { logging("error while copying stream packet"); return -1; }
153+
av_packet_unref(*pkt);
154+
return 0;
155+
}
156+
157+
158+
int main(int argc, char *argv[])
159+
{
160+
StreamingParams sp = {0};
161+
sp.copy_audio = 1;
162+
sp.copy_video = 1;
163+
164+
StreamingContext *decoder = (StreamingContext*) calloc(1, sizeof(StreamingContext));
165+
decoder->filename = argv[1];
166+
167+
StreamingContext *encoder = (StreamingContext*) calloc(1, sizeof(StreamingContext));
168+
encoder->filename = argv[2];
169+
170+
/*
171+
* Opening the decodable media file
172+
*/
173+
if (open_media(decoder->filename, &decoder->avfc)) return -1;
174+
175+
/*
176+
* Prepping the decoder
177+
*/
178+
if (prepare_decoder(decoder)) return -1;
179+
180+
/*
181+
* Opening the output media file
182+
*/
183+
//https://www.ffmpeg.org/doxygen/trunk/avformat_8h.html#a0234fa1116af3c0a72edaa08a2ba304f
184+
// Allocate an AVFormatContext for an output format.
185+
avformat_alloc_output_context2(&encoder->avfc, NULL, NULL, encoder->filename);
186+
if (!encoder->avfc) {logging("could not allocate memory for output format");return -1;}
187+
188+
/*
189+
* Prepping the encoder
190+
*/
191+
192+
// for video
193+
if (!sp.copy_video) {
194+
// transcoding
195+
// TODO:
196+
} else {
197+
// just copying
198+
if (prepare_copy(encoder->avfc, &encoder->video_avs, decoder->video_avs->codecpar)) {return -1;}
199+
}
200+
201+
// for audio
202+
if (!sp.copy_audio) {
203+
// transcoding
204+
// TODO:
205+
} else {
206+
// just copying
207+
if (prepare_copy(encoder->avfc, &encoder->audio_avs, decoder->audio_avs->codecpar)) {return -1;}
208+
}
209+
210+
/*
211+
* Prepping the output media file
212+
*/
213+
if (encoder->avfc->oformat->flags & AVFMT_GLOBALHEADER)
214+
//https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga9ed82634c59b339786575827c47a8f68
215+
// Place global headers in extradata instead of every keyframe.
216+
encoder->avfc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
217+
218+
if (!(encoder->avfc->oformat->flags & AVFMT_NOFILE)) {
219+
//https://www.ffmpeg.org/doxygen/trunk/aviobuf_8c.html#ab1b99c5b70aa59f15ab7cd4cbb40381e
220+
// Create and initialize a AVIOContext for accessing the resource indicated by url.
221+
if (avio_open(&encoder->avfc->pb, encoder->filename, AVIO_FLAG_WRITE) < 0) {
222+
logging("could not open the output file");
223+
return -1;
224+
}
225+
}
226+
//https://www.ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga18b7b10bb5b94c4842de18166bc677cb
227+
// Allocate the stream private data and write the stream header to an output media file.
228+
if (avformat_write_header(encoder->avfc, NULL) < 0) {logging("an error occurred when opening output file"); return -1;}
229+
230+
/*
231+
* Reading the streams packets from the input media file
232+
*/
233+
AVFrame *input_frame = av_frame_alloc();
234+
if (!input_frame) {logging("failed to allocated memory for AVFrame"); return -1;}
235+
236+
AVPacket *input_packet = av_packet_alloc();
237+
if (!input_packet) {logging("failed to allocated memory for AVPacket"); return -1;}
238+
239+
int response = 0;
240+
241+
//https://www.ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#ga4fdb3084415a82e3810de6ee60e46a61
242+
// Return the next frame of a stream.
243+
while (av_read_frame(decoder->avfc, input_packet) >= 0)
244+
{
245+
if (decoder->avfc->streams[input_packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
246+
if (!sp.copy_video) {
247+
// transcoding
248+
// TODO
249+
} else {
250+
// just copying
251+
if (remux(&input_packet, &encoder->avfc, decoder->video_avs->time_base, encoder->video_avs->time_base)) return -1;
252+
}
253+
} else if (decoder->avfc->streams[input_packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
254+
if (!sp.copy_audio) {
255+
// transcoding
256+
// TODO
257+
} else {
258+
// just copying
259+
if (remux(&input_packet, &encoder->avfc, decoder->audio_avs->time_base, encoder->audio_avs->time_base)) return -1;
260+
}
261+
} else {
262+
logging("ignoring all non video or audio packets");
263+
}
264+
}
265+
//https://www.ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga7f14007e7dc8f481f054b21614dfec13
266+
// Write the stream trailer to an output media file and free the file private data.
267+
av_write_trailer(encoder->avfc);
268+
269+
// TODO: we should free everything!
270+
return 0;
271+
}
272+

Makefile

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
usage:
2+
echo "make fetch_small_bunny_video && make run_hello"
3+
4+
all: clean fetch_bbb_video make_hello run_hello make_remuxing run_remuxing_ts run_remuxing_fragmented_mp4 make_transcoding
5+
.PHONY: all
6+
17
clean:
28
@rm -rf ./build/*
39

@@ -24,3 +30,12 @@ run_remuxing_ts: make_remuxing
2430

2531
run_remuxing_fragmented_mp4: make_remuxing
2632
docker run -w /files --rm -it -v `pwd`:/files leandromoreira/ffmpeg-devel /files/build/remuxing /files/small_bunny_1080p_60fps.mp4 /files/fragmented_small_bunny_1080p_60fps.mp4 fragmented
33+
34+
make_transcoding: clean
35+
docker run -w /files --rm -it -v `pwd`:/files leandromoreira/ffmpeg-devel \
36+
gcc -W -L/opt/ffmpeg/lib -I/opt/ffmpeg/include/ /files/3_transcoding.c /files/video_debugging.c \
37+
-lavcodec -lavformat -lavfilter -lavdevice -lswresample -lswscale -lavutil \
38+
-o /files/build/3_transcoding
39+
40+
run_transcoding: make_transcoding
41+
docker run -w /files --rm -it -v `pwd`:/files leandromoreira/ffmpeg-devel ./build/3_transcoding /files/small_bunny_1080p_60fps.mp4 /files/bunny_1s_gop.mp4

video_debugging.c

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include <libavcodec/avcodec.h>
2+
#include <libavformat/avformat.h>
3+
#include <libavutil/timestamp.h>
4+
#include <stdio.h>
5+
#include <stdarg.h>
6+
#include <stdlib.h>
7+
#include <libavutil/opt.h>
8+
#include <string.h>
9+
#include <inttypes.h>
10+
#include "video_debugging.h"
11+
12+
void logging(const char *fmt, ...)
13+
{
14+
va_list args;
15+
fprintf( stderr, "LOG: " );
16+
va_start( args, fmt );
17+
vfprintf( stderr, fmt, args );
18+
va_end( args );
19+
fprintf( stderr, "\n" );
20+
}
21+
22+
void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
23+
{
24+
AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
25+
26+
logging("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d",
27+
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
28+
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
29+
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
30+
pkt->stream_index);
31+
}
32+
33+
void print_timing(char *name, AVFormatContext *avf, AVCodecContext *avc, AVStream *avs) {
34+
logging("=================================================");
35+
logging("%s", name);
36+
37+
logging("\tAVFormatContext");
38+
if (avf != NULL) {
39+
logging("\t\tstart_time=%d duration=%d bit_rate=%d start_time_realtime=%d", avf->start_time, avf->duration, avf->bit_rate, avf->start_time_realtime);
40+
} else {
41+
logging("\t\t->NULL");
42+
}
43+
44+
logging("\tAVCodecContext");
45+
if (avc != NULL) {
46+
logging("\t\tbit_rate=%d ticks_per_frame=%d width=%d height=%d gop_size=%d keyint_min=%d sample_rate=%d profile=%d level=%d ",
47+
avc->bit_rate, avc->ticks_per_frame, avc->width, avc->height, avc->gop_size, avc->keyint_min, avc->sample_rate, avc->profile, avc->level);
48+
logging("\t\tavc->time_base=num/den %d/%d", avc->time_base.num, avc->time_base.den);
49+
logging("\t\tavc->framerate=num/den %d/%d", avc->framerate.num, avc->framerate.den);
50+
logging("\t\tavc->pkt_timebase=num/den %d/%d", avc->pkt_timebase.num, avc->pkt_timebase.den);
51+
} else {
52+
logging("\t\t->NULL");
53+
}
54+
55+
logging("\tAVStream");
56+
if (avs != NULL) {
57+
logging("\t\tindex=%d start_time=%d duration=%d ", avs->index, avs->start_time, avs->duration);
58+
logging("\t\tavs->time_base=num/den %d/%d", avs->time_base.num, avs->time_base.den);
59+
logging("\t\tavs->sample_aspect_ratio=num/den %d/%d", avs->sample_aspect_ratio.num, avs->sample_aspect_ratio.den);
60+
logging("\t\tavs->avg_frame_rate=num/den %d/%d", avs->avg_frame_rate.num, avs->avg_frame_rate.den);
61+
logging("\t\tavs->r_frame_rate=num/den %d/%d", avs->r_frame_rate.num, avs->r_frame_rate.den);
62+
} else {
63+
logging("\t\t->NULL");
64+
}
65+
66+
logging("=================================================");
67+
}

video_debugging.h

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <libavcodec/avcodec.h>
2+
#include <libavformat/avformat.h>
3+
#include <libavutil/timestamp.h>
4+
#include <stdio.h>
5+
#include <stdarg.h>
6+
#include <stdlib.h>
7+
#include <libavutil/opt.h>
8+
#include <string.h>
9+
#include <inttypes.h>
10+
11+
void logging(const char *fmt, ...);
12+
void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt);
13+
void print_timing(char *name, AVFormatContext *avf, AVCodecContext *avc, AVStream *avs);

0 commit comments

Comments
 (0)